command line support (partial) via PEB stomping

This update include support to passing command line parameters to unmanaged exe via PEB stomping.
This technique is not working with every executable since it depends on which functions are used to pass arguments.
Generally, to get a universally working technique would be required to hook GetCommandlineA GetCommandlineW __getmainargs and __wgetmainargs since PEB stomping won't cover all cases, more details here:
https://blog-30cm-tw.translate.goog/2020/08/windows-c-mainargc-argv.html?_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=it&_x_tr_pto=wapp

However, during my testing I found that mimikatz and several go binaries are working just by doing PEB stomping.
On the other hand, cmdline passing via PEB stomping alone to mingw and VS compiled binaries won't likely work.
This commit is contained in:
naksyn
2023-07-27 06:44:29 -07:00
parent 63ebe1c4ba
commit db1893910c
160 changed files with 70537 additions and 42 deletions
+127
View File
@@ -0,0 +1,127 @@
import windows
from windows import winproxy
import windows.generated_def as gdef
import ctypes
from windows.pycompat import is_py3
if is_py3:
from multiprocessing.connection import PipeConnection as native_PipeConnection
else:
from _multiprocessing import PipeConnection as native_PipeConnection
# Inspired from 'multiprocessing\connection.py'
def full_pipe_address(addr):
"""Return the full address of the pipe `addr`"""
if not isinstance(addr, bytes):
addr = addr.encode("ascii")
if addr.startswith(b"\\\\"):
return addr
return br"\\.\pipe" + "\\".encode() + addr
class PipeConnection(object): # Cannot inherit: crash the interpreter
"""A wrapper arround :class:`_multiprocessing.PipeConnection` able to work as a ContextManager"""
BUFFER_SIZE = 0x2000
def __init__(self, connection, name=None, server=False):
self.handle = connection.fileno()
self.connection = connection
self.name = name
self.server = server
@classmethod
def from_handle(cls, phandle, *args, **kwargs):
"""Create a :class:`PipeConnection` from pipe handle `phandle`"""
connection = native_PipeConnection(phandle)
return cls(connection, *args, **kwargs)
@classmethod
def create(cls, addr, security_descriptor=None):
"""Create a namedpipe pipe ``addr``
:returns type: :class:`PipeConnection`
"""
addr = full_pipe_address(addr)
security_attributes = None
if security_descriptor is not None:
if isinstance(security_descriptor, str):
security_descriptor = windows.security.SecurityDescriptor.from_string(security_descriptor)
security_attributes = gdef.SECURITY_ATTRIBUTES()
security_attributes.nLength = ctypes.sizeof(security_attributes)
security_attributes.lpSecurityDescriptor = security_descriptor # Accept as arg ?
security_attributes.bInheritHandle = True # Accept as arg ?
pipehandle = winproxy.CreateNamedPipeA(
addr, gdef.PIPE_ACCESS_DUPLEX,
gdef.PIPE_TYPE_MESSAGE | gdef.PIPE_READMODE_MESSAGE |
gdef.PIPE_WAIT,
gdef.PIPE_UNLIMITED_INSTANCES, cls.BUFFER_SIZE, cls.BUFFER_SIZE,
gdef.NMPWAIT_WAIT_FOREVER, security_attributes
)
return cls.from_handle(pipehandle, name=addr, server=True)
@classmethod
def connect(cls, addr):
"""Connect to the named pipe ``addr``
:returns type: :class:`PipeConnection`
"""
addr = full_pipe_address(addr)
pipehandle = winproxy.CreateFileA(addr, gdef.GENERIC_READ | gdef.GENERIC_WRITE, 0, None, gdef.OPEN_EXISTING, 0, None)
winproxy.SetNamedPipeHandleState(pipehandle, gdef.ULONG(gdef.PIPE_READMODE_MESSAGE), None, None)
return cls.from_handle(pipehandle, name=addr, server=False)
def send(self, *args, **kwargs):
"""Send an object on the pipe"""
return self.connection.send(*args, **kwargs)
def recv(self, *args, **kwargs):
"""Send an object from the pipe"""
return self.connection.recv(*args, **kwargs)
def wait_connection(self):
"""Wait for a client process to connect to the named pipe"""
return winproxy.ConnectNamedPipe(self.handle, None)
def get_security_descriptor(self):
return windows.security.SecurityDescriptor.from_handle(self.handle)
def set_security_descriptor(self, sd):
if isinstance(sd, basestring):
sd = windows.security.SecurityDescriptor.from_string(sd)
sd._apply_to_handle_and_type(self.handle)
security_descriptor = property(get_security_descriptor, set_security_descriptor)
def close(self):
"""Close the handle of the pipe"""
self.connection.close()
self.handle = None
def __enter__(self):
return self
def __exit__(self, *args, **kwargs):
self.close()
def __repr__(self):
return """<{0} name="{1}" server={2}>""".format(type(self).__name__, self.name, self.server)
connect = PipeConnection.connect
create = PipeConnection.create
def send_object(addr, obj):
"""Send `obj` on pipe ``addr``"""
with connect(addr) as np:
np.send(obj)
return None
def recv_object(addr):
"""Receive an object from pipe ``addr``"""
with create(addr) as np:
np.wait_connection()
return np.recv()