Adapt sample + docs for microsoft-store-python-injection

This commit is contained in:
hakril
2025-01-28 19:45:36 +01:00
parent 082b13112d
commit 92b3cfcb2b
4 changed files with 88 additions and 22 deletions
+14
View File
@@ -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
"""""
@@ -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 <c:\\program files\\windowsapps\\pythonsoftwarefoundation.python.3.13_3.13.496.0_x64__qbz5n2kfra8p0\\vcruntime140.dll> 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 "<string>", line 1, in <module>\nNameError: name \'BAD_VARIABLE\' is not defined\n'
Sleeping a little
Killing target process !
@@ -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("<I", process.read_memory(mem, ctypes.sizeof(ctypes.c_uint)))[0]
data = process.read_memory(mem + ctypes.sizeof(ctypes.c_uint), size)
return data
print("Starting target")
# First: show what happen when injecting mspython normally
print("Trying normal execute_python()")
proc1 = windows.utils.create_process(r"C:\Windows\system32\winver.exe")
try:
proc1.execute_python("2 + 2 == 5")
except Exception as e:
print(" Exception during proc1.execute_python():")
print(" {0}".format(repr(e)))
proc1.exit()
print("Trying mspython workaround:")
proc_info = PROCESS_INFORMATION()
StartupInfo = STARTUPINFOW()
StartupInfo.cb = ctypes.sizeof(StartupInfo)
windows.winproxy.CreateProcessW(
r"C:\Windows\system32\winver.exe",
dwCreationFlags=CREATE_SUSPENDED,
r"C:\Windows\system32\winver.exe",
dwCreationFlags=CREATE_SUSPENDED,
# Point PYTHONHOME to the interpreter dir so non-DLL libs can load
# Point PYTHONPATH to the newly created cache directory so DLL libs are loaded from there
lpEnvironment=('\0'.join('{}={}'.format(e, v) for e, v in os.environ.items()) + \
'\0PYTHONHOME={}\0PYTHONPATH={}\0\0'.format(INTERPRETER_DIR, CACHE_DIR)).encode(),
lpProcessInformation=ctypes.byref(proc_info),
lpProcessInformation=ctypes.byref(proc_info),
lpStartupInfo=ctypes.byref(StartupInfo))
process = windows.winobject.process.WinProcess(pid=proc_info.dwProcessId, handle=proc_info.hProcess)
print("Executing python code!")
print(" Executing python code!")
safe_execute_python(process, """
import windows
windows.utils.create_console()
@@ -103,9 +130,19 @@ print('hello from inside the suspended process!', flush=True)
process.threads[0].resume()
print("Executing more python code!")
print(" Executing more python code!")
safe_execute_python(process, """
print('hello from inside the resumed process!', flush=True)
""")
process.wait()
print(" Executing an error python code!")
try:
safe_execute_python(process, """BAD_VARIABLE""")
except RemotePythonError as e:
print(" Expected error during safe_execute_python")
print(" {0}".format(e))
print(" Sleeping a little")
time.sleep(5)
print(" Killing target process !")
process.exit()
+2 -2
View File
@@ -1134,7 +1134,7 @@ class WinProcess(Process):
.. note::
This method is incompatible with Microsoft Store builds of python, as the interpreter DLLs do not grant execute to Users.
See workaround: https://github.com/hakril/PythonForWindows/tree/master/samples/process/msstore_interpreter_remote_python.py
See workaround: https://hakril.github.io/PythonForWindows/build/html/sample.html#microsoft-store-python-injection
"""
return injection.safe_execute_python(self, pycode)
@@ -1145,7 +1145,7 @@ class WinProcess(Process):
.. note::
This method is incompatible with Microsoft Store builds of python, as the interpreter DLLs do not grant execute to Users.
See workaround: https://github.com/hakril/PythonForWindows/tree/master/samples/process/msstore_interpreter_remote_python.py
See workaround: https://hakril.github.io/PythonForWindows/build/html/sample.html#microsoft-store-python-injection
"""
return injection.execute_python_code(self, pycode)