mirror of
https://github.com/naksyn/PythonMemoryModule
synced 2026-06-06 16:24:25 +00:00
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:
@@ -0,0 +1,119 @@
|
||||
from os import urandom
|
||||
|
||||
from windows import winproxy
|
||||
from windows.crypto import DEFAULT_ENCODING
|
||||
from windows.generated_def import *
|
||||
|
||||
__all__ = ["encrypt", "decrypt"]
|
||||
|
||||
def encode_init_vector(data):
|
||||
blob = CRYPT_DATA_BLOB.from_string(data)
|
||||
size = DWORD()
|
||||
buf = None
|
||||
winproxy.CryptEncodeObjectEx(DEFAULT_ENCODING, X509_OCTET_STRING, ctypes.byref(blob), 0, None, buf, size)
|
||||
buf = (BYTE * size.value)()
|
||||
winproxy.CryptEncodeObjectEx(DEFAULT_ENCODING, X509_OCTET_STRING, ctypes.byref(blob), 0, None, buf, size)
|
||||
return buf[:]
|
||||
|
||||
|
||||
class GenerateInitVector(object):
|
||||
def __repr__(self):
|
||||
return "GenerateInitVector()"
|
||||
|
||||
def generate_init_vector(self, algo):
|
||||
if algo in [szOID_OIWSEC_desCBC, szOID_RSA_DES_EDE3_CBC]:
|
||||
return urandom(8)
|
||||
if algo in [szOID_NIST_AES128_CBC, szOID_NIST_AES192_CBC, szOID_NIST_AES256_CBC]:
|
||||
return urandom(16)
|
||||
return None
|
||||
geninitvector = GenerateInitVector()
|
||||
|
||||
|
||||
def encrypt(cert_or_certlist, msg, algo=szOID_NIST_AES256_CBC, initvector=geninitvector):
|
||||
"""Encrypt ``msg`` one or many :class:`Certificate` using ``algo`` with the initial
|
||||
vector ``initvector``.
|
||||
|
||||
If ``geninitvector`` is left as it is, it will generate a random one.
|
||||
|
||||
Algorithms supported by ``GenerateInitVector`` are:
|
||||
|
||||
* ``szOID_OIWSEC_desCBC``
|
||||
* ``szOID_RSA_DES_EDE3_CBC``
|
||||
* ``szOID_NIST_AES128_CBC``
|
||||
* ``szOID_NIST_AES192_CBC``
|
||||
* ``szOID_NIST_AES256_CBC``
|
||||
|
||||
:param cert_or_certlist: One or many :class:`Certificate` used to encrypt the msg
|
||||
:type cert_or_certlist: :class:`Certificate` | [:class:`Certificate`]
|
||||
:return: :class:`bytearray`: The encrypted message
|
||||
"""
|
||||
alg_ident = CRYPT_ALGORITHM_IDENTIFIER()
|
||||
alg_ident.pszObjId = algo.encode("ascii")
|
||||
# We want to have automatique translation of Certificate -> PCERT_CONTEXT
|
||||
# In order to simple create the 'PCERT_CONTEXT[] certs'
|
||||
# For that we need a tuple of X * 1-item-tuple
|
||||
# as a (cert,) will be automaticly translatable to a PCERT_CONTEXT
|
||||
if isinstance(cert_or_certlist, CERT_CONTEXT):
|
||||
certlist = ((cert_or_certlist,),)
|
||||
else:
|
||||
certlist = tuple((c,) for c in cert_or_certlist)
|
||||
|
||||
# Set (compute if needed) the IV
|
||||
if initvector is None:
|
||||
alg_ident.Parameters.cbData = 0
|
||||
elif initvector is geninitvector:
|
||||
initvector = initvector.generate_init_vector(algo)
|
||||
if initvector is None:
|
||||
raise ValueError("I don't know how to generate an <initvector> for <{0}> please provide one (or None)".format(algo))
|
||||
initvector_encoded = encode_init_vector(initvector)
|
||||
alg_ident.Parameters = CRYPT_DATA_BLOB.from_string(initvector_encoded)
|
||||
else:
|
||||
initvector_encoded = encode_init_vector(initvector)
|
||||
alg_ident.Parameters = CRYPT_DATA_BLOB.from_string(initvector_encoded)
|
||||
|
||||
# Setup encryption parameters
|
||||
param = CRYPT_ENCRYPT_MESSAGE_PARA()
|
||||
param.cbSize = ctypes.sizeof(param)
|
||||
param.dwMsgEncodingType = DEFAULT_ENCODING
|
||||
param.hCryptProv = None
|
||||
param.ContentEncryptionAlgorithm = alg_ident
|
||||
param.pvEncryptionAuxInfo = None
|
||||
param.dwFlags = 0
|
||||
param.dwInnerContentType = 0
|
||||
|
||||
|
||||
certs = (PCERT_CONTEXT * len(certlist))(*certlist)
|
||||
#Ask the output buffer size
|
||||
size = DWORD()
|
||||
winproxy.CryptEncryptMessage(param, len(certs), certs, msg, len(msg), None, size)
|
||||
#Encrypt the msg
|
||||
buf = (BYTE * size.value)()
|
||||
winproxy.CryptEncryptMessage(param, len(certs), certs, msg, len(msg), buf, size)
|
||||
return bytearray(buf[:size.value])
|
||||
|
||||
|
||||
def decrypt(cert_store, encrypted):
|
||||
"""Try to decrypt the ``encrypted`` msg with any certificate in ``cert_store``.
|
||||
|
||||
If there is no certificate able to decrypt the message ``WinproxyError(winerror=0x8009200c)`` is raised.
|
||||
|
||||
:param cert_store:
|
||||
:type cert_store: :class:`CertificateStore`
|
||||
:return: :class:`str`: The decrypted message
|
||||
"""
|
||||
# Setup decryption parameters
|
||||
dparam = CRYPT_DECRYPT_MESSAGE_PARA()
|
||||
dparam.cbSize = ctypes.sizeof(dparam)
|
||||
dparam.dwMsgAndCertEncodingType = DEFAULT_ENCODING
|
||||
dparam.cCertStore = 1
|
||||
dparam.rghCertStore = (cert_store,)
|
||||
dparam.dwFlags = 0
|
||||
|
||||
#Ask the output buffer size
|
||||
buf = (BYTE * len(encrypted)).from_buffer_copy(encrypted)
|
||||
dcryptsize = DWORD()
|
||||
winproxy.CryptDecryptMessage(dparam, buf, ctypes.sizeof(buf), None, dcryptsize, None)
|
||||
#Decrypt the msg
|
||||
dcryptbuff = (BYTE * (dcryptsize.value + 0x1000))()
|
||||
winproxy.CryptDecryptMessage(dparam, buf, ctypes.sizeof(buf), dcryptbuff, dcryptsize, None)
|
||||
return bytes(bytearray(dcryptbuff[:dcryptsize.value]))
|
||||
Reference in New Issue
Block a user