Files
naksyn-PythonMemoryModule/pythonmemorymodule/windows/wintrust.py
T
naksyn db1893910c 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.
2023-07-27 06:44:29 -07:00

192 lines
6.5 KiB
Python

import ctypes
import struct
import windows
from collections import namedtuple
from windows import winproxy
import windows.generated_def as gdef
from windows.generated_def.winstructs import *
# From: um/SoftPub.h
WINTRUST_ACTION_GENERIC_VERIFY_V2 = gdef.IID.from_string("00AAC56B-CD44-11d0-8CC2-00C04FC295EE")
DRIVER_ACTION_VERIFY = gdef.IID.from_string("F750E6C3-38EE-11d1-85E5-00C04FC295EE")
wintrust_know_return_value = [
TRUST_E_PROVIDER_UNKNOWN,
TRUST_E_ACTION_UNKNOWN,
TRUST_E_SUBJECT_FORM_UNKNOWN,
DIGSIG_E_ENCODE,
TRUST_E_SUBJECT_NOT_TRUSTED,
TRUST_E_BAD_DIGEST,
DIGSIG_E_DECODE,
DIGSIG_E_EXTENSIBILITY,
PERSIST_E_SIZEDEFINITE,
DIGSIG_E_CRYPTO,
PERSIST_E_SIZEINDEFINITE,
PERSIST_E_NOTSELFSIZING,
TRUST_E_NOSIGNATURE,
CERT_E_EXPIRED,
CERT_E_VALIDITYPERIODNESTING,
CERT_E_PURPOSE,
CERT_E_ISSUERCHAINING,
CERT_E_MALFORMED,
CERT_E_UNTRUSTEDROOT,
CERT_E_CHAINING,
TRUST_E_FAIL,
CERT_E_REVOKED,
CERT_E_UNTRUSTEDTESTROOT,
CERT_E_REVOCATION_FAILURE,
CERT_E_CN_NO_MATCH,
CERT_E_WRONG_USAGE,
TRUST_E_EXPLICIT_DISTRUST,
CERT_E_UNTRUSTEDCA,
CERT_E_INVALID_POLICY,
CERT_E_INVALID_NAME,
CRYPT_E_FILE_ERROR,
]
wintrust_return_value_mapper = gdef.FlagMapper(*wintrust_know_return_value)
def check_signature(filename):
"""Check if ``filename`` embeds a valid signature.
:return: :class:`int`: ``0`` if ``filename`` have a valid signature else the error
"""
file_data = WINTRUST_FILE_INFO()
file_data.cbStruct = ctypes.sizeof(WINTRUST_FILE_INFO)
file_data.pcwszFilePath = filename
file_data.hFile = None
file_data.pgKnownSubject = None
WVTPolicyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2
win_trust_data = WINTRUST_DATA()
win_trust_data.cbStruct = ctypes.sizeof(WINTRUST_DATA)
win_trust_data.pPolicyCallbackData = None
win_trust_data.pSIPClientData = None
win_trust_data.dwUIChoice = WTD_UI_NONE
# win_trust_data.fdwRevocationChecks = WTD_REVOKE_NONE
win_trust_data.fdwRevocationChecks = WTD_REVOKE_WHOLECHAIN
win_trust_data.dwUnionChoice = WTD_CHOICE_FILE
win_trust_data.dwStateAction = WTD_STATEACTION_VERIFY
win_trust_data.hWVTStateData = None
win_trust_data.pwszURLReference = None
win_trust_data.dwUIContext = 0
#win_trust_data.dwProvFlags = 0x1000 + 0x10 + 0x800
win_trust_data.tmp_union.pFile = ctypes.pointer(file_data)
x = winproxy.WinVerifyTrust(None, ctypes.byref(WVTPolicyGUID), ctypes.byref(win_trust_data))
win_trust_data.dwStateAction = WTD_STATEACTION_CLOSE
winproxy.WinVerifyTrust(None, ctypes.byref(WVTPolicyGUID), ctypes.byref(win_trust_data))
return wintrust_return_value_mapper[x & 0xffffffff]
def get_catalog_for_filename(filename):
ctx = HCATADMIN()
winproxy.CryptCATAdminAcquireContext(ctypes.byref(ctx), DRIVER_ACTION_VERIFY, 0)
hash = get_file_hash(filename)
if hash is None:
return None
t = winproxy.CryptCATAdminEnumCatalogFromHash(ctx, hash, len(hash), 0, None)
if t is None:
return None
tname = get_catalog_name_from_handle(t)
while t is not None:
t = winproxy.CryptCATAdminEnumCatalogFromHash(ctx, hash, len(hash), 0, ctypes.byref(HCATINFO(t)))
# Todo: how to handle multiple catalog ?
winproxy.CryptCATAdminReleaseCatalogContext(ctx, t, 0)
winproxy.CryptCATAdminReleaseContext(ctx, 0)
return tname
def get_file_hash(filename):
f = open(filename, "rb")
handle = windows.utils.get_handle_from_file(f)
size = DWORD(0)
x = winproxy.CryptCATAdminCalcHashFromFileHandle(handle, ctypes.byref(size), None, 0)
buffer = (BYTE * size.value)()
try:
x = winproxy.CryptCATAdminCalcHashFromFileHandle(handle, ctypes.byref(size), buffer, 0)
except WindowsError as e:
if e.winerror == 1006:
# CryptCATAdminCalcHashFromFileHandle: [Error 1006]
# The volume for a file has been externally altered so that the opened file is no longer valid.
# (returned for empty file)
return None
raise
return buffer
def get_file_hash2(filename): #POC: name/API will change/disapear
f = open(filename, "rb")
handle = windows.utils.get_handle_from_file(f)
cathand = HANDLE()
h = winproxy.CryptCATAdminAcquireContext2(cathand, None, "SHA256", None, 0)
print(cathand)
size = DWORD(0)
x = winproxy.CryptCATAdminCalcHashFromFileHandle2(cathand, handle, ctypes.byref(size), None, 0)
buffer = (BYTE * size.value)()
try:
x = winproxy.CryptCATAdminCalcHashFromFileHandle2(cathand, handle, ctypes.byref(size), buffer, 0)
except WindowsError as e:
if e.winerror == 1006:
# CryptCATAdminCalcHashFromFileHandle: [Error 1006]
# The volume for a file has been externally altered so that the opened file is no longer valid.
# (returned for empty file)
return None
raise
return buffer
def get_catalog_name_from_handle(handle):
cat_info = CATALOG_INFO()
cat_info.cbStruct = ctypes.sizeof(cat_info)
winproxy.CryptCATCatalogInfoFromContext(handle, ctypes.byref(cat_info), 0)
return cat_info.wszCatalogFile
SignatureData = namedtuple("SignatureData", ["signed", "catalog", "catalogsigned", "additionalinfo"])
"""Signature information for ``FILENAME``:
* ``signed``: True if ``FILENAME`` embeds a valide signature
* ``catalog``: The filename of the catalog ``FILENAME`` is part of (if any)
* ``catalogsigned``: True if ``catalog`` embeds a valide signature
* ``additionalinfo``: The return error of ``check_signature(FILENAME)``
``additionalinfo`` is useful to know if ``FILENAME`` signature was rejected for an invalid root / expired cert.
"""
def full_signature_information(filename):
"""Returns more information about the signature of ``filename``
:return: :class:`SignatureData`
"""
check_sign = check_signature(filename)
signed = not bool(check_sign)
catalog = get_catalog_for_filename(filename)
if catalog is None:
return SignatureData(signed, None, False, check_sign)
catalogsigned = not bool(check_signature(catalog))
return SignatureData(signed, catalog, catalogsigned, check_sign)
def is_signed(filename):
"""Check if ``filename`` is signed:
* File embeds a valid signature
* File is part of a signed catalog file
:return: :class:`bool`
"""
check_sign = check_signature(filename)
if check_sign == 0:
return True
catalog = get_catalog_for_filename(filename)
if catalog is None:
return False
catalogsigned = not bool(check_signature(catalog))
return catalogsigned