One shot, Triple kill: Pwning all three Google kernelCTF instances with a single 1-day Linux vulnerability

We discussed this vulnerability during Episode 226 on 21 November 2023

One vulnerability a use-after-free in the Linux nftable subsystem, exploitable on the three kernelCTF targets: latest Long-term Stable (LTS) release, Container-optimized build as used by Google Cloud, and a Mitigation build that isn’t as up-to-date but includes experimentation mitigations to be bypassed.

The vulnerability exists in the Netfilter tables subsystem of the Linux kernel. The issue occurs during processing of a NFT_MSG_NEWRULE operation inside of a transaction/batch; as the name implies you are adding a new rule to a set. if an error happens during this it can fall into the err_release_rule path, which calls into nf_tables_rule_release Which makes sense from a developer point of view, the rule is bad, you want to release it. However this function calls into nft_rule_expr_deactivate which takes in a parameter for the current phase. It is hard-coded to use the NFT_TRANS_RELEASE phase so when the function is called, for that phase it’ll end up unbinding the nft_set object the rule was being added to. However a reference to that set is still kept earlier in the chain processing the transaction, leading to the use-after-free.

The patch seems fairly straight forward, rather than using the nf_tables_rule_release function, they call the two functions that function would call, and change the phase for the call to nft_rule_expr_deactivate to the appropriate NFT_TRANS_PREPARE

With this vulnerability there is the initial use-after-free, but if execution keeps going, the prematurely freed nft_set structure will be freed again after everything has been processed creating a double free situation. A double-free is a much more friendly primitive to have for exploitation so the authors pursued that route. They did have to introduce an extra set object into the process to interweave the frees in order to bypass a naive double-free check (can’t free the same pointer twice in a row).

I won’t be diving too far into the exploitation here because usage of themsg_msg and msg_msgseg structures has been well explored. It is a very powerful object that can be sprayed from userland with a high-degree of control over the data by a user. Ultimately they corrupt the pipe_buf_operations structure which contains various function pointers which can be triggered from operations on the pipe in userland. And then went for a ROP chain to escalate privileges.

I will call out one thing I found kinda fun, while on the LTS kernelCTF box they did a standard escalation via a commit_creds call. On the Cloud-optimized build, while they used some different objects for their corruption, they still corrupted an operations structure and got in position from a ROP. Instead of doing a commit_creds call they called set_memory_x to set some heap memory as executable and just ran plain shellcode they wrote into the heap that did the usual escalation technique.