CVE-2021-43267 TIPC协议MSG_CRYPTO消息溢出利用

阅读量462137

|评论1

|

发布时间 : 2021-12-16 10:30:13

 

影响版本:Linux v5.10-rc1 ~ v5.14.15。v5.14.16已修补。高危,可导致远程提权,评分9.8。 默认不加载,需用户配置。

测试版本:Linux-5.14.15 exploit及测试环境下载地址https://github.com/bsauce/kernel-exploit-factory

编译选项: 配置所有**TIPC**选项,CONFIG_TIPC=y**NETLINK**选项。

在编译时将.config中的CONFIG_E1000CONFIG_E1000E,变更为=y。参考

exp原作者关闭了 CONFIG_SLAB_FREELIST_RANDOMCONFIG_SLAB_FREELIST_HARDENED,难怪我的测试总是不能成功。如何在有CONFIG_SLAB_FREELIST_HARDENED保护的情况下提高利用成功率呢?

# 安装 pahole, 否则 CONFIG_DEBUG_INFO_BTF 不成功。 但是安装完后还是报错,所以修改.config 中 CONFIG_DEBUG_INFO_BTF=n 来关闭BTF。
$ git clone https://git.kernel.org/pub/scm/devel/pahole/pahole.git/
$ cd pahole
$ mkcd build
$ sudo apt-get install cmake libdw-dev zlib1g zlib1g-dev libelf-dev dwarves
$ cmake -D__LIB=lib ..
$ sudo make install

$ sudo apt-get install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc qemu qemu-system bison flex libncurses5-dev libelf-dev
$ wget https://mirrors.tuna.tsinghua.edu.cn/kernel/v4.x/linux-5.14.15.tar.xz
$ tar -xvf linux-5.14.15.tar.xz
# KASAN: 设置 make menuconfig 设置"Kernel hacking" ->"Memory Debugging" -> "KASan: runtime memory debugger"。
$ make -j32
$ make all
$ make modules
# 编译出的bzImage目录:/arch/x86/boot/bzImage。

# 问题:make[1]: *** No rule to make target 'debian/canonical-certs.pem'
# 解决:修改 .config, 将 CONFIG_SYSTEM_TRUSTED_KEYS 和 CONFIG_SYSTEM_REVOCATION_KEYS 置空即可
#      CONFIG_DEBUG_INFO_BTF=n

漏洞描述:漏洞位于 net/tipc/crypto.c 文件,TIPC(Transparent Inter-Process Communication)集群内通信协议中对 MSG_CRYPTO 类型的消息长度验证出错,导致堆溢出。tipc_crypto_key_rcv() 函数中,TIPC消息(tipc_msg结构)的数据部分指向MSG_CRYPTO消息(tipc_aead_key结构),在分配tipc_aead_key 空间并拷贝 tipc_aead_key->key 时,未校验tipc_aead_key->keylen的有效性,导致拷贝越界。只对TIPC消息的 header sizemsg size 进行检查,却没有对 MSG_CRYPTO消息的tipc_aead_key->keylen进行检查。

补丁patch 在拷贝 MSG_CRYPTO 消息之前检查 keylen 和 size。

net/tipc/crypto.c | 32 +++++++++++++++++++++-----------
 1 file changed, 21 insertions(+), 11 deletions(-)

 diff --git a/net/tipc/crypto.c b/net/tipc/crypto.c
 index c9391d38d..dc60c32bb 100644
 --- a/net/tipc/crypto.c
 +++ b/net/tipc/crypto.c
 @@ -2285,43 +2285,53 @@ static bool tipc_crypto_key_rcv(struct tipc_crypto *rx, struct tipc_msg *hdr)
   u16 key_gen = msg_key_gen(hdr);
   u16 size = msg_data_sz(hdr);
   u8 *data = msg_data(hdr);

 +   unsigned int keylen;
 +
 +   /* Verify whether the size can exist in the packet */
 +   if (unlikely(size < sizeof(struct tipc_aead_key) + TIPC_AEAD_KEYLEN_MIN)) {
 +       pr_debug("%s: message data size is too small\n", rx->name);
 +       goto exit;
 +   }
 +
 +   keylen = ntohl(*((__be32 *)(data + TIPC_AEAD_ALG_NAME)));
 +
 +   /* Verify the supplied size values */
 +   if (unlikely(size != keylen + sizeof(struct tipc_aead_key) ||
 +            keylen > TIPC_AEAD_KEY_SIZE_MAX)) {
 +       pr_debug("%s: invalid MSG_CRYPTO key size\n", rx->name);
 +       goto exit;
 +   }

     spin_lock(&rx->lock);
     if (unlikely(rx->skey || (key_gen == rx->key_gen && rx->key.keys))) {
     pr_err("%s: key existed <%p>, gen %d vs %d\n", rx->name,
        rx->skey, key_gen, rx->key_gen);
 -        goto exit;
 +        goto exit_unlock;
     }
     /* Allocate memory for the key */
     skey = kmalloc(size, GFP_ATOMIC);
     if (unlikely(!skey)) {
     pr_err("%s: unable to allocate memory for skey\n", rx->name);
 -        goto exit;
 +        goto exit_unlock;
     }

     /* Copy key from msg data */
 -   skey->keylen = ntohl(*((__be32 *)(data + TIPC_AEAD_ALG_NAME)));
 +   skey->keylen = keylen;
    memcpy(skey->alg_name, data, TIPC_AEAD_ALG_NAME);
    memcpy(skey->key, data + TIPC_AEAD_ALG_NAME + sizeof(__be32),
            skey->keylen);

 -   /* Sanity check */
 -   if (unlikely(size != tipc_aead_key_size(skey))) {
 -       kfree(skey);
 -       skey = NULL;
 -       goto exit;
 -   }
 -
    rx->key_gen = key_gen;
    rx->skey_mode = msg_key_mode(hdr);
    rx->skey = skey;
    rx->nokey = 0;
    mb(); /* for nokey flag */

 - exit:
 + exit_unlock:
    spin_unlock(&rx->lock);
 + exit:
    /* Schedule the key attaching on this crypto */
    if (likely(skey && queue_delayed_work(tx->wq, &rx->work, 0)))
    return true;

保护机制:SMEP / SMAP / KASLR / KPTI

利用总结

  • (1)使用netlink来使能 UDP bearer media
  • (2)创建有效的node link: 发送 LINK_CONFIG / LINK_PROTOCOL / LINK_PROTOCOL 包;
  • (3)创建 /tmp/benign 错误elf文件, 触发modprobe; 创建 /tmp/hax 提权文件, modprobe_path 将指向 "/tmp/hax"
  • (4)堆喷布置2048个 msg_msg 消息, 释放偶数下标的 msg_msg
  • (5)触发tipc漏洞,泄露内核基址;
    • (5-1)触发 tipc 漏洞, 篡改 msg_msg->m_ts
    • (5-2)喷射 0x40 个 tty_struct 结构,便于越界读泄露;
    • (5-3)msg_msg 越界读泄露 tty_struct->opstty_struct->ldisc_sem->read_wait->next(若读取到tty_struct->magic 也即0x5401,则表示泄露成功);
  • (6)篡改 tty_struct->ops.ioctl 指针, 并篡改 modprobe_path"/tmp/hax"
    • (6-1)msg_msg 消息偏移0x60处放置gadget mov qword ptr [rdx], rsi 地址(伪造tty_operations->ioctl指针),喷射2048个这样的msg_msg
    • (6-2)篡改 tty_struct->ops 指针,指向某个 msg_msg 的消息开头;
    • (6-3)调用 ioctl 触发任意写gadget,篡改 modprobe_path"/tmp/hax"
  • (7)执行 /tmp/hax 触发modprobe并提权。

1. 漏洞分析

1-1. TIPC协议介绍

简介:TIPC(Transparent Inter Process Communication)是一种专为集群内通信而设计的协议。消息传递是顺序保证、无丢失和流控制的。延迟时间比任何其他已知协议都短,而最大吞吐量可与 TCP 相媲美。

配置:默认不加载,需用户来加载。加载时,TPIC可以用作socket并可以用Netlink接口来配置,它可以配置为通过 UDP 或直接通过Ethernet以太网传输消息。但是低权限的用户无法创建Ethernet帧,所以使用UDP更容易实现本地漏洞利用。

协议头:虽然TIPC是在这些协议之上运行的,但有独立的地址方案,节点可以选择自己的地址。所有的消息构造和分析都是在kernel中进行的。每个TIPC消息都有相同的通用header格式和消息特定的header,TIPC消息(tipc_msg结构)存储地址位于sk_buff->data指向的线性数据区。对该漏洞来说,通用header最重要的部分是Header Sizemessage size

TIPC消息的header如下所示:

tipc_msg_validate() 函数负责验证这两个size的大小。 调用路径 —— tipc_rcv() -> tipc_msg_validate()

bool tipc_msg_validate(struct sk_buff **_skb)
{
    struct sk_buff *skb = *_skb;
    struct tipc_msg *hdr;
    int msz, hsz;

    /* Ensure that flow control ratio condition is satisfied */
    if (unlikely(skb->truesize / buf_roundup_len(skb) >= 4)) {
        skb = skb_copy_expand(skb, BUF_HEADROOM, 0, GFP_ATOMIC);
        if (!skb)
            return false;
        kfree_skb(*_skb);
        *_skb = skb;
    }

    if (unlikely(TIPC_SKB_CB(skb)->validated))
        return true;

    if (unlikely(!pskb_may_pull(skb, MIN_H_SIZE)))
        return false;

    hsz = msg_hdr_sz(buf_msg(skb));                    // <------ [1-1] header size     buf_msg(skb) 返回 skb->data指针(存储数据包的内容和各层协议头, 这里是tipc_msg结构, 参考CVE-2017-6074对sk_buff的分析); msg_hdr_sz() 取 hsz = (tipc_msg->hdr[0]>>21 & 0xf) << 2, 注意乘了4
    if (unlikely(hsz < MIN_H_SIZE) || (hsz > MAX_H_SIZE))        // 满足 hsz 在 24~60 之间
        return false;
    if (unlikely(!pskb_may_pull(skb, hsz)))
        return false;

    hdr = buf_msg(skb);
    if (unlikely(msg_version(hdr) != TIPC_VERSION))
        return false;

    msz = msg_size(hdr);                            // <------ [1-2] msg size    msg_size() 取 msz = tipc_msg->hdr[0] & 0x1fff
    if (unlikely(msz < hsz))
        return false;
    if (unlikely((msz - hsz) > TIPC_MAX_USER_MSG_SIZE))            // 满足 msz-hsz<66000
        return false;
    if (unlikely(skb->len < msz))                    // [1-3]    // 满足 skb->len > msz; 注意skb->len表示数据区的总长度: (tail - data) + 分片结构体数据区的长度
        return false;

    TIPC_SKB_CB(skb)->validated = 1;
    return true;
}
// TIPC 消息结构
struct tipc_msg {
    __be32 hdr[15];
};
static inline u32 msg_hdr_sz(struct tipc_msg *m)
{
    return msg_bits(m, 0, 21, 0xf) << 2;
}
static inline struct tipc_msg *buf_msg(struct sk_buff *skb)
{
    return (struct tipc_msg *)skb->data;
}

TIPC消息头检查总结

  • [1-2]处 —— TIPC消息的 hszheader size 在 24~60 字节之间;
  • [1-2]处 —— mszmsg size 满足 skb->len > msz(注意skb->len表示数据区的总长度:(tail – data) + 分片结构体数据区的长度)。

1-2. 漏洞分析

MSG_CRYPTO 消息结构:2020年9月引入了一个新的用户消息类型—— MSG_CRYPTO,该消息类型允许节点发送加密的秘钥。该消息结构如下所示:tipc_aead_key MSG_CRYPTO消息跟在TIPC消息头之后(偏移24处开始)。

#define TIPC_AEAD_ALG_NAME        (32)
struct tipc_aead_key {
    char alg_name[TIPC_AEAD_ALG_NAME];        // TIPC_AEAD_ALG_NAME 32
    unsigned int keylen;    /* in bytes */
    char key[];
};

MSG_CRYPTO 消息处理:消息接收后,TIPC kernel模块需要复制该信息到该节点来存储:tipc_crypto_key_rcv()

调用路径:(struct tipc_media udp_media_inf -> tipc_udp_enable() -> tipc_udp_recv()) | tipc_l2_rcv_msg() -> tipc_rcv() -> tipc_link_rcv() -> tipc_link_input() -> tipc_data_input() -> tipc_crypto_msg_rcv() -> tipc_crypto_key_rcv()

static bool tipc_crypto_key_rcv(struct tipc_crypto *rx, struct tipc_msg *hdr)
{
    struct tipc_crypto *tx = tipc_net(rx->net)->crypto_tx;
    struct tipc_aead_key *skey = NULL;
    u16 key_gen = msg_key_gen(hdr);
    u16 size = msg_data_sz(hdr);                // [2-1] size = msg_size(m) - msg_hdr_sz(m)    = msz - hsz
    u8 *data = msg_data(hdr);                    // data = *hdr + hsz    MSG_CRYPTO消息的拷贝起始地址,也即 tipc_aead_key 结构的起始地址

    spin_lock(&rx->lock);
    if (unlikely(rx->skey || (key_gen == rx->key_gen && rx->key.keys))) {
        pr_err("%s: key existed <%p>, gen %d vs %d\n", rx->name,
               rx->skey, key_gen, rx->key_gen);
        goto exit;
    }

    /* Allocate memory for the key */
    skey = kmalloc(size, GFP_ATOMIC);            // [2-2] 分配空间,分配的 size =  msg - hsz, 而 msz 和 hsz 都来自 TIPC 消息 (tipc_msg 结构)
    if (unlikely(!skey)) {
        pr_err("%s: unable to allocate memory for skey\n", rx->name);
        goto exit;
    }

    /* Copy key from msg data */
    skey->keylen = ntohl(*((__be32 *)(data + TIPC_AEAD_ALG_NAME))); // [2-3] 拷贝长度 skey->keylen 来自偏移 *(*data+32), 未对该值进行范围检查
    memcpy(skey->alg_name, data, TIPC_AEAD_ALG_NAME);                // [2-4] 拷贝算法名称, TIPC_AEAD_ALG_NAME = 32字节
    memcpy(skey->key, data + TIPC_AEAD_ALG_NAME + sizeof(__be32),    // [2-5] 拷贝密钥, 越界 
           skey->keylen);

    /* Sanity check */
    if (unlikely(size != tipc_aead_key_size(skey))) {                // [2-6] 必须满足 size == sizeof(*skey) + skey->keylen, 这个检查已经晚了, 溢出已经发生了!!!!!
        kfree(skey);
        skey = NULL;
        goto exit;
    }

    rx->key_gen = key_gen;
    rx->skey_mode = msg_key_mode(hdr);
    rx->skey = skey;
    rx->nokey = 0;
    mb(); /* for nokey flag */

exit:
    spin_unlock(&rx->lock);

    /* Schedule the key attaching on this crypto */
    if (likely(skey && queue_delayed_work(tx->wq, &rx->work, 0)))
        return true;

    return false;
}

MSG_CRYPTO消息处理漏洞总结:TIPC消息(tipc_msg结构)的数据部分指向MSG_CRYPTO消息(tipc_aead_key结构),在分配tipc_aead_key 空间并拷贝 tipc_aead_key->key 时,未校验tipc_aead_key->keylen的有效性,导致拷贝越界。拷贝越界之后才检查keylen的有效性,为时已晚——[2-6]。只对TIPC消息的 header sizemsg size 进行检查,却没有对 MSG_CRYPTO消息的tipc_aead_key->keylen进行检查。

利用思路:攻击者可以创建一个tipc_msg->size 较小的TIPC消息来分配堆内存,再设置tipc_aead_key->keylen较大的MSG_CRYPTO 消息来触发越界写,触发漏洞的 MSG_CRYPTO 消息示例如下:


2. 触发漏洞

2-1. 交互方式

shell交互:编译时开启TIPC或者加载TIPC模块后,可通过shell来与TIPC交互,采用iproute2中的tipc命令(可以采用buildroot来编译文件系统,开启iproute2选项后,文件系统中就含有tipc命令)。使能UDP bearer media的具体命令是tipc bearer enable media udp name <NAME> localip <SOMELOCALIP>

code交互:用户层可以使用netlink消息(AF_TIPC地址族)来使能UDP bearer media,非特权用户也可以。所以即使没有配置TIPC,也能完成利用。

2-2. 到达漏洞点

思路:首先我们要使自己成为一个有效的node,并创建一个link,接着触发MSG_CRYPTO代码路径。参考TIPC的protocol specification页面,可以了解传输、寻址、数据分片的细节。

方法:exp作者通过PCAP抓包分析tipc-over-udp session建立的过程,最后确定了我们发送消息前必发的几个包。总的来说,一个正常的TIPC数据包前面包含一个header,也即6个32字节(大端序),我们标记为w0~w5。w0编码了TIPC版本、header size、payload size、message protocol,还有个flag表示是否为顺序消息;w1表示协议消息的类型;w3包含一个node_id,表示node的标识符,一般用IPv4地址表示node_id。可通过net/tipc/msg.h 文件来了解header格式。

创建有效node link过程:发送3个包。

  • LINK_CONFIG 包会广告自己;
  • LINK_PROTOCOL 包(RESET_MSG消息类型)会重置link;
  • LINK_PROTOCOL 包(STATE_MSG消息类型)会提出link;
  • 现在可以发送MSG_CRYPTO TIPC 包来触发堆溢出。
protocol: LINK_CONFIG   -> message type: DSC_REQ_MSG
protocol: LINK_PROTOCOL -> message type: RESET_MSG
protocol: LINK_PROTOCOL -> message type: STATE_MSG

2-3. 触发漏洞

方法:前面已经分析过,MSG_CRYPTO 消息的堆块分配的size来自 tipc_msg->msz - tipc_msg->hszMSG_CRYPTO 消息的拷贝的size来自 tipc_aead_key->keylen 。所以通过控制 tipc_msg->msz 即可控制目标堆块的分配大小,控制 tipc_aead_key->keylen 即可控制堆块的溢出长度。

// TIPC 消息:   (&tipc_msg + tipc_msg->hsz) 指向 MSG_CRYPTO 消息
struct tipc_msg {
    __be32 hdr[15];
};
// MSG_CRYPTO 消息
struct tipc_aead_key {
    char alg_name[TIPC_AEAD_ALG_NAME];
    unsigned int keylen;
    char key[];
};

TIPC消息布置如下:注意hsz的大小需左移2位,也即乘以4。tipc消息 = tipc_msg + tipc_aead_key


3. 绕过KALSR

泄露对象:采用ELOISE提出的Elastic Objects,例如CVE-2021-22555中也用到过的msg_msg。篡改msg_msg->m_ts 就能调用 msgrcv 进行OOB读。

/* one msg_msg structure for each message */
struct msg_msg {
    struct list_head m_list;
    long m_type;
    size_t m_ts;        /* message text size */
    struct msg_msgseg *next;
    void *security;
    /* the actual message follows immediately */
};

问题:覆写msg_msg->m_ts的同时也会覆盖msg_msg->m_list,调用msgrcv时会将匹配的消息从链表中unlink,如果不正确构造msg_msg->m_list就会导致访问崩溃。解决办法是在调用msgrcv时传递MSG_COPY flag,避免对目标消息进行unlink。

思路:在空间上布置出 msg_msg -> msg_msg -> interesting_object,然后释放第1个msg_msg ,分配MSG_CRYPTO key堆块占据第1个msg_msg,再利用MSG_CRYPTO key的溢出来篡改第2个msg_msgmsg_msg->m_ts,越界读取 interesting_object 来泄露内核函数指针。

victim对象interesting_object 选取 tty_struct,我们可以通过magic值来确认我们泄露的结构是否为有效的tty_struct (预期值为TTY_MAGIC-0x5401),struct tty_operations *ops 指向内核的.data段中某处,也即 tty_operations,可泄露内核基址,绕过KASLR。

泄露内核基址:泄露的tty_struct可能属于master ptyslave pty,所以泄露的地址可能是 ptm_unix98_opspty_unix98_ops

泄露堆地址:泄露 tty_struct 也能获得 tty_struct 的地址,因为 tty_struct->ldisc_sem->read_wait->next 指向 next 指针自身。tty_struct->ldisc_sem->read_wait->next 的偏移为0x38,减去0x408就是msg_msg数据部分的基址。

struct tty_struct {
    int magic;
    struct kref kref;
    struct device *dev; /* class device or NULL (e.g. ptys, serdev) */
    struct tty_driver *driver;
    const struct tty_operations *ops;
    int index;
    ...
}

堆喷布局如下


4. 提权

劫持RIP:劫持tty_operations。首先喷射一堆fake tty_operations,然后根据上一步释放的堆指针来猜测其中一个fake tty_operations的地址。

  • 重复分配msg_msg、分配tty_struct、释放msg_msg、触发TIPC漏洞来覆盖tty_struct
  • 为了确认是否成功覆盖tty_struct,可以尝试通过ioctl来调用tty_struct.ops.ioctl,如果成功劫持ops指针,就能控制RIP,否则调用close()关闭pty。

任意写ROP:构造ROP链很难恢复现场,所以决定采用一条gadget来进行任意写。ioctl调用的原型为int (*ioctl)(struct tty_struct *tty, unsigned int cmd, unsigned long arg),可控制32位的cmd和64位的arg参数,也就是RSIRDX。任意写的gadget如下所示:本gadget还能将rax 清零,便于判断是否成功伪造tty_operations并劫持RIP。

$ objdump -D -j .text ./vmlinux_small \
    | grep -C1 'mov    %rsi,(%rdx)' \
    | grep -B2 ret
..
ffffffff812c51f5:       31 c0                   xor    %eax,%eax
ffffffff812c51f7:       48 89 32                mov    %rsi,(%rdx)
ffffffff812c51fa:       c3                      ret
..

提权:传统方法是构造任意读和任意写来篡改cred,本文未采用这种方法。本文篡改modprobe_path(特权用户可以通过/proc/sys/kernel/modprobe来篡改)来执行恶意程序。如果执行一个magic值未知的binary,request_module最后会调用call_modprobe来发起一个基于modprobe_path的modprobe进程。

/*
 * cycle the list of binary formats handler, until one recognizes the image
 */
static int search_binary_handler(struct linux_binprm *bprm)
{
    ..
    if (request_module("binfmt-%04x", *(ushort *)(bprm->buf + 2)) < 0)
    ..
}

堆喷布局如下


5. 改进exp

问题:exp作者编译的内核版本是 5.15.0,能够找到含 xor eax, eax (能反映执行是否成功)和 mov dword ptr [rdx], rsi 的gadget,但我编译的5.14.15版本内核不含该gadget。

解决:gadget中不需要含有 xor eax, eax 也能成功提权,见 exp1.c。成功提权截图如下:

尝试别的gadget:全部尝试都失败,看来目前只能修改 modprobe_path 了。exp原作者修改 modprobe_path 的方式非常好,如果通过rop链来修改modprobe_path的话,还需要保存现场,无法保存rsp原先的值,因为不能用xchg rsp, xxx 这样的gadget。

// 首先执行到ioctl(), 发现 $rdi / $rbp / $r15 指向tty_struct, 可控
// (1) mov rsp, rdi/rbp/r15                没找到
// (2) mov rsp, qword ptr [rdi/rbp/r15  没找到    
// (3) bridging gadget: regcache_mark_dirty(), 但是找不到 mov cr4, rdi
void regcache_mark_dirty(struct regmap *map)
{
    map->lock(map->lock_arg);
    map->cache_dirty = true;
    map->no_sync_defaults = true;
    map->unlock(map->lock_arg);
}
EXPORT_SYMBOL_GPL(regcache_mark_dirty);
// (4) push rdi/rbp/r15      pop rsp       不能破坏tty_struct前8字节(会导致ioctl调用失败),所以pop rsp之后再pop两次
john@ubuntu:~/Desktop/tmp/CVE-2021-43267/file$ cat ./g1 | grep "push rdi" | grep "pop rsp"
0xffffffff8165f1aa: push rdi; add byte ptr [rdi], cl; or ebx, dword ptr [rbx + 0x41]; pop rsp; ret; 
0xffffffff814a353a: push rdi; add ebx, dword ptr [rbx + 0x41]; pop rsp; pop rbp; ret;  
john@ubuntu:~/Desktop/tmp/CVE-2021-43267/file$ cat ./g1 | grep "push rbp" | grep "pop rsp"
0xffffffff81131122: push rbp; add byte ptr [rbp + 0x41], bl; pop rsp; pop r13; ret; 
0xffffffff81aa41e4: push rbp; add byte ptr [rbp + 0x41], bl; pop rsp; ret; 
0xffffffff819ea4df: push rbp; cmp byte ptr [rbp + 0x41], bl; pop rsp; pop r13; pop r14; ret; 
0xffffffff810406a6: push rbp; or byte ptr [rbp + 0x41], bl; pop rsp; ret; 
    // (4) 的问题是找不到 mov cr4, xxx 这个gadget, 从 v5.3.1开始 就不能使用 native_write_cr4() 来修改 cr4 了
// v5.14.15
static const unsigned long cr4_pinned_mask =
    X86_CR4_SMEP | X86_CR4_SMAP | X86_CR4_UMIP | X86_CR4_FSGSBASE;
void native_write_cr4(unsigned long val)
{
    unsigned long bits_changed = 0;

set_register:
    asm volatile("mov %0,%%cr4": "+r" (val) : : "memory");

    if (static_branch_likely(&cr_pinning)) {
        if (unlikely((val & cr4_pinned_mask) != cr4_pinned_bits)) {
            bits_changed = (val & cr4_pinned_mask) ^ cr4_pinned_bits;
            val = (val & ~cr4_pinned_mask) | cr4_pinned_bits;
            goto set_register;
        }
        /* Warn after we've corrected the changed bits. */
        WARN_ONCE(bits_changed, "pinned CR4 bits changed: 0x%lx!?\n",
              bits_changed);
    }
}
// v5.2.21
static inline void native_write_cr4(unsigned long val)
{
    asm volatile("mov %0,%%cr4": : "r" (val), "m" (__force_order));
}

参考

Exploiting CVE-2021-43267 — 漏洞利用者

NVD-CVE-2021-43267-detail

CVE-2021-43267: Linux TIPC模块任意代码执行漏洞

CVE-2021-43267: Remote Linux Kernel Heap Overflow | TIPC Module Allows Arbitrary Code Execution — 漏洞发现者

Transparent Inter Process Communication Protocol — TIPC协议介绍

本文由bsauce原创发布

转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/262124

安全客 - 有思想的安全新媒体

分享到:微信
+15赞
收藏
bsauce
分享到:微信

发表评论

内容需知
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 三六零数字安全科技集团有限公司 安全客 All Rights Reserved 京ICP备08010314号-66