Skip to content

Commit

Permalink
Add kernelCTF CVE-2023-52620_lts_cos_mitigation (#117)
Browse files Browse the repository at this point in the history
* Add kernelCTF CVE-2023-52620_lts_cos_mitigation

* update exploit

* update exploit

* change Makefile

* update exploit.md exploit.c

* update exploit.md

---------

Co-authored-by: Mingi Cho <[email protected]>
  • Loading branch information
mingi and Mingi Cho authored Dec 10, 2024
1 parent 5408da0 commit 9f97ffa
Show file tree
Hide file tree
Showing 19 changed files with 3,985 additions and 0 deletions.
401 changes: 401 additions & 0 deletions pocs/linux/kernelctf/CVE-2023-52620_lts_cos_mitigation/docs/exploit.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Novel Techniques

## Bypassing kernelCTF SLAB Mitigation Using Page Allocator

The mitigation kernel in the kernelCTF applies three mitigations to SLAB: `CONFIG_SLAB_VIRTUAL`, `CONFIG_KMALLOC_SPLIT_VARSIZE` and `CONFIG_RANDOM_KMALLOC_CACHES`. These mitigations are applied in the `kmalloc_slab` when allocating objects using `kmalloc` [2].

```c
static __always_inline
void *__do_kmalloc_node(size_t size, gfp_t flags, int node, unsigned long caller)
{
struct kmem_cache *s;
void *ret;

if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) {
ret = __kmalloc_large_node(size, flags, node); // [1]
trace_kmalloc(caller, ret, size,
PAGE_SIZE << get_order(size), flags, node);
return ret;
}

s = kmalloc_slab(size, flags); // [2]

if (unlikely(ZERO_OR_NULL_PTR(s)))
return s;

ret = __kmem_cache_alloc_node(s, flags, node, size, caller);
ret = kasan_kmalloc(s, ret, size, flags);
trace_kmalloc(caller, ret, size, s->size, flags, node);
return ret;
}

void *__kmalloc_node(size_t size, gfp_t flags, int node)
{
return __do_kmalloc_node(size, flags, node, _RET_IP_);
}
EXPORT_SYMBOL(__kmalloc_node);

void *__kmalloc(size_t size, gfp_t flags)
{
return __do_kmalloc_node(size, flags, NUMA_NO_NODE, _RET_IP_);
}
EXPORT_SYMBOL(__kmalloc);
```
However, slab uses a maximum size of 0x2000 (8192), so when `kmalloc` allocates an object with a size larger than 0x2000, it uses the `__kmalloc_large_node` to allocate the object [1].
```c
/*
* To avoid unnecessary overhead, we pass through large allocation requests
* directly to the page allocator. We use __GFP_COMP, because we will need to
* know the allocation order to free the pages properly in kfree.
*/
static void *__kmalloc_large_node(size_t size, gfp_t flags, int node)
{
struct page *page;
void *ptr = NULL;
unsigned int order = get_order(size);
if (unlikely(flags & GFP_SLAB_BUG_MASK))
flags = kmalloc_fix_flags(flags);
flags |= __GFP_COMP;
page = alloc_pages_node(node, flags, order);
if (page) {
ptr = page_address(page);
mod_lruvec_page_state(page, NR_SLAB_UNRECLAIMABLE_B,
PAGE_SIZE << order);
}
ptr = kasan_kmalloc_large(ptr, size, flags);
/* As ptr might get tagged, call kmemleak hook after KASAN. */
kmemleak_alloc(ptr, size, 1, flags);
kmsan_kmalloc_large(ptr, size, flags);
return ptr;
}
```

In `__kmalloc_large_node`, memory is allocated directly from the `page allocator` without using SLAB for optimization. Therefore, the above three mitigations do not apply to objects allocated in `__kmalloc_large_node`.

```c
static struct nft_rule_blob *nf_tables_chain_alloc_rules(unsigned int size)
{
struct nft_rule_blob *blob;

/* size must include room for the last rule */
if (size < offsetof(struct nft_rule_dp, data))
return NULL;

size += sizeof(struct nft_rule_blob) + sizeof(struct nft_rules_old);
if (size > INT_MAX)
return NULL;

blob = kvmalloc(size, GFP_KERNEL_ACCOUNT); // [3]
if (!blob)
return NULL;

blob->size = 0;
nft_last_rule(blob, blob->data);

return blob;
}
```
The exploit uses a blob object from `nft_chain`, which can be allocated to arbitrary size by the user in `nf_tables_chain_alloc_rules` [3]. Therefore, we can bypass the mitigation, allocate a blob object larger than 0x2000 and spray it using another larger than 0x2000 object.
```c
static int ____sys_sendmsg(struct socket *sock, struct msghdr *msg_sys,
unsigned int flags, struct used_address *used_address,
unsigned int allowed_msghdr_flags)
{
...
} else if (ctl_len) {
BUILD_BUG_ON(sizeof(struct cmsghdr) !=
CMSG_ALIGN(sizeof(struct cmsghdr)));
if (ctl_len > sizeof(ctl)) {
ctl_buf = sock_kmalloc(sock->sk, ctl_len, GFP_KERNEL); // [4]
if (ctl_buf == NULL)
goto out;
}
err = -EFAULT;
if (copy_from_user(ctl_buf, msg_sys->msg_control_user, ctl_len))
goto out_freectl;
msg_sys->msg_control = ctl_buf;
msg_sys->msg_control_is_user = false;
}
...
```

We used `ctl_buf` from `____sys_sendmsg` to spray the chain's blob object. Since `ctf_buf` also allows the user to allocate objects of arbitrary size, we were able to allocate a size larger than 0x2000 `[4]`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
- Requirements:
- Capabilities: CAP_NET_ADMIN
- Kernel configuration: CONFIG_NETFILTER, CONFIG_NF_TABLES
- User namespaces required: Yes
- Introduced by: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=761da2935d6e18d178582dbdf315a3a458555505 (netfilter: nf_tables: add set timeout API support)
- Fixed by: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e26d3009efda338f19016df4175f354a9bd0a4ab (netfilter: nf_tables: disallow timeout for anonymous sets)
- Affected Version: v4.1-rc1 - v6.4-rc7
- Affected Component: net/netfilter
- Cause: Use-After-Free
- Syscall to disable: disallow unprivileged username space
- URL: https://cve.mitre.org/cgi-bin/cvename.cgi?name=2023-52620
- Description: In the Linux kernel, the following vulnerability has been resolved: netfilter: nf_tables: disallow timeout for anonymous sets Never used from userspace, disallow these parameters.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
LIBMNL_DIR = $(realpath ./)/libmnl_build
LIBNFTNL_DIR = $(realpath ./)/libnftnl_build

exploit:
gcc -o exploit exploit.c -L$(LIBNFTNL_DIR)/install/lib -L$(LIBMNL_DIR)/install/lib -lnftnl -lmnl -I$(LIBNFTNL_DIR)/libnftnl-1.2.5/include -I$(LIBMNL_DIR)/libmnl-1.0.5/include -static -s

prerequisites: libmnl-build libnftnl-build

libmnl-build : libmnl-download
tar -C $(LIBMNL_DIR) -xvf $(LIBMNL_DIR)/libmnl-1.0.5.tar.bz2
cd $(LIBMNL_DIR)/libmnl-1.0.5 && ./configure --enable-static --prefix=`realpath ../install`
cd $(LIBMNL_DIR)/libmnl-1.0.5 && make
cd $(LIBMNL_DIR)/libmnl-1.0.5 && make install

libnftnl-build : libmnl-build libnftnl-download
tar -C $(LIBNFTNL_DIR) -xvf $(LIBNFTNL_DIR)/libnftnl-1.2.5.tar.xz
cd $(LIBNFTNL_DIR)/libnftnl-1.2.5 && PKG_CONFIG_PATH=$(LIBMNL_DIR)/install/lib/pkgconfig ./configure --enable-static --prefix=`realpath ../install`
cd $(LIBNFTNL_DIR)/libnftnl-1.2.5 && C_INCLUDE_PATH=$(C_INCLUDE_PATH):$(LIBMNL_DIR)/install/include LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):$(LIBMNL_DIR)/install/lib make
cd $(LIBNFTNL_DIR)/libnftnl-1.2.5 && make install

libmnl-download :
mkdir $(LIBMNL_DIR)
wget -P $(LIBMNL_DIR) https://netfilter.org/projects/libmnl/files/libmnl-1.0.5.tar.bz2

libnftnl-download :
mkdir $(LIBNFTNL_DIR)
wget -P $(LIBNFTNL_DIR) https://netfilter.org/projects/libnftnl/files/libnftnl-1.2.5.tar.xz

run:
./exploit
./exploit
./exploit

clean:
rm -rf $(LIBMNL_DIR)
rm -rf $(LIBNFTNL_DIR)
rm -f exploit
Binary file not shown.
Loading

0 comments on commit 9f97ffa

Please sign in to comment.