From 92b3cfcb2b68a669fb33cc10a40382fbfe07d2de Mon Sep 17 00:00:00 2001 From: hakril Date: Tue, 28 Jan 2025 19:45:36 +0100 Subject: [PATCH] Adapt sample + docs for microsoft-store-python-injection --- docs/source/sample.rst | 14 ++++ ...cess_msstore_interpreter_remote_python.txt | 15 ++++ .../msstore_interpreter_remote_python.py | 77 ++++++++++++++----- windows/winobject/process.py | 4 +- 4 files changed, 88 insertions(+), 22 deletions(-) create mode 100644 docs/source/samples_output/process_msstore_interpreter_remote_python.txt diff --git a/docs/source/sample.rst b/docs/source/sample.rst index dc255cf..50c93a2 100644 --- a/docs/source/sample.rst +++ b/docs/source/sample.rst @@ -69,6 +69,20 @@ Output .. _token_sample: + +Microsoft Store Python Injection +'''''''''''''''''''''''''''''''' + +Python execution in remote process fails with Microsoft Store builds of pythons (`mspython`), as the interpreter DLLs do not grant execute to Users. +This sample shows a workaround by user https://github.com/dariushoule by copying needed mspython files to a temporary directory and injecting those instead. + +.. literalinclude:: ..\..\samples\process\msstore_interpreter_remote_python.py + +Output + +.. literalinclude:: samples_output\process_msstore_interpreter_remote_python.txt + + Token """"" diff --git a/docs/source/samples_output/process_msstore_interpreter_remote_python.txt b/docs/source/samples_output/process_msstore_interpreter_remote_python.txt new file mode 100644 index 0000000..f175f9c --- /dev/null +++ b/docs/source/samples_output/process_msstore_interpreter_remote_python.txt @@ -0,0 +1,15 @@ +PS C:\Users\hakril\PythonForWindows> py .\samples\process\msstore_interpreter_remote_python.py +Executable is: C:\Users\hakril\AppData\Local\Microsoft\WindowsApps\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\python.exe +Trying normal execute_python() + Exception during proc1.execute_python(): + InjectionFailedError('Injection of failed') +Trying mspython workaround: + Executing python code! +Injecting: C:\Users\hakril\AppData\Local\Temp\pfw_dllcache\vcruntime140.dll +Injecting: C:\Users\hakril\AppData\Local\Temp\pfw_dllcache\python313.dll + Executing more python code! + Executing an error python code! + Expected error during safe_execute_python + b'Traceback (most recent call last):\n File "", line 1, in \nNameError: name \'BAD_VARIABLE\' is not defined\n' + Sleeping a little + Killing target process ! \ No newline at end of file diff --git a/samples/process/msstore_interpreter_remote_python.py b/samples/process/msstore_interpreter_remote_python.py index 184bea6..032f548 100644 --- a/samples/process/msstore_interpreter_remote_python.py +++ b/samples/process/msstore_interpreter_remote_python.py @@ -1,7 +1,7 @@ -# Some python interpreters run in environments with restrictive ACLs (no Users/* execute) on bundled DLLs. -# The Microsoft Store version of python is the prime example of this. -# -# Remote execution of python is still possible by creating a minimal set of the dependencies outside of the restricted directory. +# Some python interpreters run in environments with restrictive ACLs (no Users/* execute) on bundled DLLs. +# The Microsoft Store version of python is the prime example of this. +# +# Remote execution of python is still possible by creating a minimal set of the dependencies outside of the restricted directory. # # This can be very helpful when operating PFW in environments with restrive GPOs / AppLocker. @@ -12,15 +12,19 @@ import os import shutil import tempfile import time +import sys +import struct import windows from windows.generated_def.ntstatus import STATUS_THREAD_IS_TERMINATING from windows.generated_def.windef import CREATE_SUSPENDED from windows.generated_def.winstructs import PROCESS_INFORMATION, STARTUPINFOW from windows.injection import RemotePythonError, \ - find_python_dll_to_inject, get_dll_name_from_python_version, inject_python_command, load_dll_in_remote_process, retrieve_last_exception_data + find_python_dll_to_inject, get_dll_name_from_python_version, inject_python_command, load_dll_in_remote_process, retrieve_exc +print("Executable is: {0}".format(sys.executable)) + CACHE_DIR = os.path.join(tempfile.gettempdir(), 'pfw_dllcache') INTERPRETER_DIR = os.path.dirname(find_python_dll_to_inject(64)) # Tailor bitness to your needs @@ -28,8 +32,8 @@ INTERPRETER_DIR = os.path.dirname(find_python_dll_to_inject(64)) # Tailor bitnes def mspython_acl_workaround(target, pydll_path): """ Works around mspython ACL restrictions on mspython interpreters - by copying the critical DLLs to a TEMP dir and orienting the interpreter - against that TEMP dir. + by copying the critical DLLs to a TEMP dir and orienting the interpreter + against that TEMP dir. """ if not os.path.exists(CACHE_DIR): @@ -40,10 +44,12 @@ def mspython_acl_workaround(target, pydll_path): try: # Creates a copy of the DLL without bringing over restrictive ACLs shutil.copyfile(dll, cache_dll_path) - except: + except Exception as e: # If its not writeable good chance these DLLs are just already loaded somewhere - pass + print(e) + # Preloading python DLL and vcruntime so they don't get loaded from the path tree with restrictive ACLs + print("Injecting: {0}".format(cache_dll_path)) load_dll_in_remote_process(target, cache_dll_path) for dll in glob.glob(os.path.join(INTERPRETER_DIR, 'dlls', '*')): @@ -51,16 +57,19 @@ def mspython_acl_workaround(target, pydll_path): try: # Dynamic lib DLLs with restrictive ACLs copied to unrestricted parent shutil.copyfile(dll, cache_dll_path) - except: - pass + except Exception as e: + print(e) + + target._workaround_applied = True # Adapted from windows\winobject\process.py def execute_python_code(process, code): py_dll_name = get_dll_name_from_python_version() pydll_path = find_python_dll_to_inject(process.bitness) - - mspython_acl_workaround(process, pydll_path) + + if not getattr(process, "_workaround_applied", None): + mspython_acl_workaround(process, pydll_path) shellcode, pythoncode = inject_python_command(process, code, py_dll_name) t = process.create_thread(shellcode, pythoncode) return t @@ -78,23 +87,41 @@ def safe_execute_python(process, code): data = retrieve_last_exception_data(process) raise RemotePythonError(data) +# Adapted from windows\injection.py +def retrieve_last_exception_data(process): + with process.allocated_memory(0x1000) as mem: + execute_python_code(process, retrieve_exc.format(mem)).wait() + size = struct.unpack("