<commit_before># CVE-2021-22555: Turning \x00\x00 into 10000$

<p align='right'><NAME> (theflow@) - Information Security Engineer</p>

CVE-2021-22555 is a 15 years old heap out-of-bounds write vulnerability in Linux Netfilter that is powerful enough to bypass all modern security mitigations and achieve kernel code execution. It was used to break the kubernetes pod isolation of the [kCTF cluster](https://google.github.io/kctf/vrp.html) and won 10000$ for charity (where Google will match and double the donation to 20000$).

<p align="center"><img src="images/poc.gif"/></p>

## Table of Contents

- [Introduction](#introduction)
- [Vulnerability](#vulnerability)
- [Exploitation](#exploitation)
  * [Exploring struct msg_msg](#exploring-struct-msg_msg)
  * [Achieving use-after-free](#achieving-use-after-free)
  * [Bypassing SMAP](#bypassing-smap)
  * [Achieving a better use-after-free](#achieving-a-better-use-after-free)
  * [Finding a victim object](#finding-a-victim-object)
  * [Bypassing KASLR/SMEP](#bypassing-kaslrsmep)
  * [Escalating privileges](#escalating-privileges)
    + [Kernel ROP chain](#kernel-rop-chain)
    + [Escaping the container and popping a root shell](#escaping-the-container-and-popping-a-root-shell)
- [Proof-Of-Concept](#proof-of-concept)
- [Timeline](#timeline)
- [Thanks](#thanks)

## Introduction

After [BleedingTooth](https://google.github.io/security-research/pocs/linux/bleedingtooth/writeup.html), which was the first time I looked into Linux, I wanted to find a privilege escalation vulnerability as well. I started by looking at old vulnerabilities like CVE-2016-3134 and CVE-2016-4997 which inspired me to grep for `memcpy()` and `memset()` in the Netfilter code. This led me to some buggy code.

## Vulnerability

When `IPT_SO_SET_REPLACE` or `IP6T_SO_SET_REPLACE` is called in compatibility mode (which needs `CAP_NET_ADMIN`), structures need to be converted from user to kernel as well as 32bit to 64bit in order to be processed by the native functions. Naturally, this is destined to be error prone. Our vulnerability is in `xt_compat_target_from_user()` where `memset()` is called with an offset `target->targetsize` that is not accounted for during the allocation - leading to a few bytes written out-of-bounds:

```c
// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/netfilter/x_tables.c
void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr,
				unsigned int *size)
{
	const struct xt_target *target = t->u.kernel.target;
	struct compat_xt_entry_target *ct = (struct compat_xt_entry_target *)t;
	int pad, off = xt_compat_target_offset(target);
	u_int16_t tsize = ct->u.user.target_size;
	char name[sizeof(t->u.user.name)];

	t = *dstptr;
	memcpy(t, ct, sizeof(*ct));
	if (target->compat_from_user)
		target->compat_from_user(t->data, ct->data);
	else
		memcpy(t->data, ct->data, tsize - sizeof(*ct));
	pad = XT_ALIGN(target->targetsize) - target->targetsize;
	if (pad > 0)
		memset(t->data + target->targetsize, 0, pad);

	tsize += off;
	t->u.user.target_size = tsize;
	strlcpy(name, target->name, sizeof(name));
	module_put(target->me);
	strncpy(t->u.user.name, name, sizeof(t->u.user.name));

	*size += off;<commit_msg>Clarify about CAP_NET_ADMIN and user namespace.<commit_after># CVE-2021-22555: Turning \x00\x00 into 10000$

<p align='right'><NAME> (theflow@) - Information Security Engineer</p>

CVE-2021-22555 is a 15 years old heap out-of-bounds write vulnerability in Linux Netfilter that is powerful enough to bypass all modern security mitigations and achieve kernel code execution. It was used to break the kubernetes pod isolation of the [kCTF cluster](https://google.github.io/kctf/vrp.html) and won 10000$ for charity (where Google will match and double the donation to 20000$).

<p align="center"><img src="images/poc.gif"/></p>

## Table of Contents

- [Introduction](#introduction)
- [Vulnerability](#vulnerability)
- [Exploitation](#exploitation)
  * [Exploring struct msg_msg](#exploring-struct-msg_msg)
  * [Achieving use-after-free](#achieving-use-after-free)
  * [Bypassing SMAP](#bypassing-smap)
  * [Achieving a better use-after-free](#achieving-a-better-use-after-free)
  * [Finding a victim object](#finding-a-victim-object)
  * [Bypassing KASLR/SMEP](#bypassing-kaslrsmep)
  * [Escalating privileges](#escalating-privileges)
    + [Kernel ROP chain](#kernel-rop-chain)
    + [Escaping the container and popping a root shell](#escaping-the-container-and-popping-a-root-shell)
- [Proof-Of-Concept](#proof-of-concept)
- [Timeline](#timeline)
- [Thanks](#thanks)

## Introduction

After [BleedingTooth](https://google.github.io/security-research/pocs/linux/bleedingtooth/writeup.html), which was the first time I looked into Linux, I wanted to find a privilege escalation vulnerability as well. I started by looking at old vulnerabilities like CVE-2016-3134 and CVE-2016-4997 which inspired me to grep for `memcpy()` and `memset()` in the Netfilter code. This led me to some buggy code.

## Vulnerability

When `IPT_SO_SET_REPLACE` or `IP6T_SO_SET_REPLACE` is called in compatibility mode, which requires the `CAP_NET_ADMIN` capability that can however be obtained in a user+network namespace, structures need to be converted from user to kernel as well as 32bit to 64bit in order to be processed by the native functions. Naturally, this is destined to be error prone. Our vulnerability is in `xt_compat_target_from_user()` where `memset()` is called with an offset `target->targetsize` that is not accounted for during the allocation - leading to a few bytes written out-of-bounds:

```c
// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/netfilter/x_tables.c
void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr,
				unsigned int *size)
{
	const struct xt_target *target = t->u.kernel.target;
	struct compat_xt_entry_target *ct = (struct compat_xt_entry_target *)t;
	int pad, off = xt_compat_target_offset(target);
	u_int16_t tsize = ct->u.user.target_size;
	char name[sizeof(t->u.user.name)];

	t = *dstptr;
	memcpy(t, ct, sizeof(*ct));
	if (target->compat_from_user)
		target->compat_from_user(t->data, ct->data);
	else
		memcpy(t->data, ct->data, tsize - sizeof(*ct));
	pad = XT_ALIGN(target->targetsize) - target->targetsize;
	if (pad > 0)
		memset(t->data + target->targetsize, 0, pad);

	tsize += off;
	t->u.user.target_size = tsize;
	strlcpy(name, target->name, sizeof(name));
	module_put(target->me);
	strncpy(t->u.user.name, name, sizeof(t->u.user.name));

	*size += off;
{'fraction_non_alphanumeric': 0.12824031716986886, 'fraction_numerical': 0.019518145776151265, 'mean_word_length': 4.995429616087751, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 55, 'https://': 6, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5508804', 'n_tokens_mistral': 2387, 'n_tokens_neox': 2168, 'n_words': 623}