# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved. # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Author: Alberto Solino (@agsolino) # # Description: # [MS-OAUT]: OLE Automation Protocol Implementation # This was used as a way to test the DCOM runtime. Further # testing is needed to verify it is working as expected # # Best way to learn how to use these calls is to grab the protocol standard # so you understand what the call does, and then read the test case located # at https://github.com/SecureAuthCorp/impacket/tree/master/tests/SMB_RPC # # Since DCOM is like an OO RPC, instead of helper functions you will see the # classes described in the standards developed. # There are test cases for them too. # from __future__ import division from __future__ import print_function import random from struct import pack, unpack from impacket import LOG from impacket import hresult_errors from impacket.dcerpc.v5.dcomrt import DCOMCALL, DCOMANSWER, IRemUnknown2, PMInterfacePointer, INTERFACE, \ MInterfacePointer, MInterfacePointer_ARRAY, BYTE_ARRAY, PPMInterfacePointer from impacket.dcerpc.v5.dtypes import LPWSTR, ULONG, DWORD, SHORT, GUID, USHORT, LONG, WSTR, BYTE, LONGLONG, FLOAT, \ DOUBLE, HRESULT, PSHORT, PLONG, PLONGLONG, PFLOAT, PDOUBLE, PHRESULT, CHAR, ULONGLONG, INT, UINT, PCHAR, PUSHORT, \ PULONG, PULONGLONG, PINT, PUINT, NULL from impacket.dcerpc.v5.enum import Enum from impacket.dcerpc.v5.ndr import NDRSTRUCT, NDRUniConformantArray, NDRPOINTER, NDRENUM, NDRUSHORT, NDRUNION, \ NDRUniConformantVaryingArray, NDR from impacket.dcerpc.v5.rpcrt import DCERPCException from impacket.uuid import string_to_bin class DCERPCSessionError(DCERPCException): def __init__(self, error_string=None, error_code=None, packet=None): DCERPCException.__init__(self, error_string, error_code, packet) def __str__( self ): if self.error_code in hresult_errors.ERROR_MESSAGES: error_msg_short = hresult_errors.ERROR_MESSAGES[self.error_code][0] error_msg_verbose = hresult_errors.ERROR_MESSAGES[self.error_code][1] return 'OAUT SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose) else: return 'OAUT SessionError: unknown error code: 0x%x' % (self.error_code) ################################################################################ # CONSTANTS ################################################################################ # 1.9 Standards Assignments IID_IDispatch = string_to_bin('00020400-0000-0000-C000-000000000046') IID_ITypeInfo = string_to_bin('00020401-0000-0000-C000-000000000046') IID_ITypeComp = string_to_bin('00020403-0000-0000-C000-000000000046') IID_NULL = string_to_bin('00000000-0000-0000-0000-000000000000') error_status_t = ULONG LCID = DWORD WORD = NDRUSHORT # 2.2.2 IID IID = GUID # 2.2.3 LPOLESTR LPOLESTR = LPWSTR OLESTR = WSTR # 2.2.4 REFIID REFIID = IID # 2.2.25 DATE DATE = DOUBLE class PDATE(NDRPOINTER): referent = ( ('Data', DATE), ) # 2.2.27 VARIANT_BOOL VARIANT_BOOL = USHORT class PVARIANT_BOOL(NDRPOINTER): referent = ( ('Data', VARIANT_BOOL), ) # 3.1.4.4 IDispatch::Invoke (Opnum 6) # dwFlags DISPATCH_METHOD = 0x00000001 DISPATCH_PROPERTYGET = 0x00000002 DISPATCH_PROPERTYPUT = 0x00000004 DISPATCH_PROPERTYPUTREF = 0x00000008 DISPATCH_zeroVarResult = 0x00020000 DISPATCH_zeroExcepInfo = 0x00040000 DISPATCH_zeroArgErr = 0x00080000 ################################################################################ # STRUCTURES ################################################################################ # 2.2.26 DECIMAL class DECIMAL(NDRSTRUCT): structure = ( ('wReserved',WORD), ('scale',BYTE), ('sign',BYTE), ('Hi32',ULONG), ('Lo64',ULONGLONG), ) class PDECIMAL(NDRPOINTER): referent = ( ('Data', DECIMAL), ) # 2.2.7 VARIANT Type Constants class VARENUM(NDRENUM): class enumItems(Enum): VT_EMPTY = 0 VT_NULL = 1 VT_I2 = 2 VT_I4 = 3 VT_R4 = 4 VT_R8 = 5 VT_CY = 6 VT_DATE = 7 VT_BSTR = 8 VT_DISPATCH = 9 VT_ERROR = 0xa VT_BOOL = 0xb VT_VARIANT = 0xc VT_UNKNOWN = 0xd VT_DECIMAL = 0xe VT_I1 = 0x10 VT_UI1 = 0x11 VT_UI2 = 0x12 VT_UI4 = 0x13 VT_I8 = 0x14 VT_UI8 = 0x15 VT_INT = 0x16 VT_UINT = 0x17 VT_VOID = 0x18 VT_HRESULT = 0x19 VT_PTR = 0x1a VT_SAFEARRAY = 0x1b VT_CARRAY = 0x1c VT_USERDEFINED = 0x1d VT_LPSTR = 0x1e VT_LPWSTR = 0x1f VT_RECORD = 0x24 VT_INT_PTR = 0x25 VT_UINT_PTR = 0x26 VT_ARRAY = 0x2000 VT_BYREF = 0x4000 VT_RECORD_OR_VT_BYREF = VT_RECORD | VT_BYREF VT_UI1_OR_VT_BYREF = VT_UI1 | VT_BYREF VT_I2_OR_VT_BYREF = VT_I2 | VT_BYREF VT_I4_OR_VT_BYREF = VT_I4 | VT_BYREF VT_I8_OR_VT_BYREF = VT_I8 | VT_BYREF VT_R4_OR_VT_BYREF = VT_R4 | VT_BYREF VT_R8_OR_VT_BYREF = VT_R8 | VT_BYREF VT_BOOL_OR_VT_BYREF = VT_BOOL | VT_BYREF VT_ERROR_OR_VT_BYREF = VT_ERROR | VT_BYREF VT_CY_OR_VT_BYREF = VT_CY | VT_BYREF VT_DATE_OR_VT_BYREF = VT_DATE | VT_BYREF VT_BSTR_OR_VT_BYREF = VT_BSTR | VT_BYREF VT_UNKNOWN_OR_VT_BYREF = VT_UNKNOWN | VT_BYREF VT_DISPATCH_OR_VT_BYREF = VT_DISPATCH | VT_BYREF VT_ARRAY_OR_VT_BYREF = VT_ARRAY | VT_BYREF VT_VARIANT_OR_VT_BYREF = VT_VARIANT| VT_BYREF VT_I1_OR_VT_BYREF = VT_I1 | VT_BYREF VT_UI2_OR_VT_BYREF = VT_UI2 | VT_BYREF VT_UI4_OR_VT_BYREF = VT_UI4 | VT_BYREF VT_UI8_OR_VT_BYREF = VT_UI8 | VT_BYREF VT_INT_OR_VT_BYREF = VT_INT | VT_BYREF VT_UINT_OR_VT_BYREF = VT_UINT | VT_BYREF VT_DECIMAL_OR_VT_BYREF = VT_DECIMAL | VT_BYREF # 2.2.8 SAFEARRAY Feature Constants class SF_TYPE(NDRENUM): # [v1_enum] type structure = ( ('Data', ' 0 THEN # PRINT Name of the method is rgBstrNames[0] # PRINT Parameters to above method are following # FOR Y = 1 to pcNames -1 # PRINT rgBstrNames[Y] # END FOR # END IF # END FOR i # ENDIF def enumerateMethods(iInterface): methods = dict() typeInfoCount = iInterface.GetTypeInfoCount() if typeInfoCount['pctinfo'] == 0: LOG.error('Automation Server does not support type information for this object') return {} iTypeInfo = iInterface.GetTypeInfo() iTypeAttr = iTypeInfo.GetTypeAttr() for x in range(iTypeAttr['ppTypeAttr']['cFuncs']): funcDesc = iTypeInfo.GetFuncDesc(x) names = iTypeInfo.GetNames(funcDesc['ppFuncDesc']['memid'], 255) print(names['rgBstrNames'][0]['asData']) funcDesc.dump() print('='*80) if names['pcNames'] > 0: name = names['rgBstrNames'][0]['asData'] methods[name] = {} for param in range(1, names['pcNames']): methods[name][names['rgBstrNames'][param]['asData']] = '' if funcDesc['ppFuncDesc']['elemdescFunc'] != NULL: methods[name]['ret'] = funcDesc['ppFuncDesc']['elemdescFunc']['tdesc']['vt'] return methods def checkNullString(string): if string == NULL: return string if string[-1:] != '\x00': return string + '\x00' else: return string class ITypeComp(IRemUnknown2): def __init__(self, interface): IRemUnknown2.__init__(self,interface) self._iid = IID_ITypeComp class ITypeInfo(IRemUnknown2): def __init__(self, interface): IRemUnknown2.__init__(self,interface) self._iid = IID_ITypeInfo def GetTypeAttr(self): request = ITypeInfo_GetTypeAttr() resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return resp def GetTypeComp(self): request = ITypeInfo_GetTypeComp() resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return ITypeComp(INTERFACE(self.get_cinstance(), ''.join(resp['ppTComp']['abData']), self.get_ipidRemUnknown(), target = self.get_target())) def GetFuncDesc(self, index): request = ITypeInfo_GetFuncDesc() request['index'] = index resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return resp def GetNames(self, memid, cMaxNames=10): request = ITypeInfo_GetNames() request['memid'] = memid request['cMaxNames'] = cMaxNames resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return resp def GetDocumentation(self, memid, refPtrFlags=15): request = ITypeInfo_GetDocumentation() request['memid'] = memid request['refPtrFlags'] = refPtrFlags resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return resp class IDispatch(IRemUnknown2): def __init__(self, interface): IRemUnknown2.__init__(self,interface) self._iid = IID_IDispatch def GetTypeInfoCount(self): request = IDispatch_GetTypeInfoCount() resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return resp def GetTypeInfo(self): request = IDispatch_GetTypeInfo() request['iTInfo'] = 0 request['lcid'] = 0 resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return ITypeInfo(INTERFACE(self.get_cinstance(), ''.join(resp['ppTInfo']['abData']), self.get_ipidRemUnknown(), target = self.get_target())) def GetIDsOfNames(self, rgszNames, lcid = 0): request = IDispatch_GetIDsOfNames() request['riid'] = IID_NULL for name in rgszNames: tmpName = LPOLESTR() tmpName['Data'] = checkNullString(name) request['rgszNames'].append(tmpName) request['cNames'] = len(rgszNames) request['lcid'] = lcid resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) IDs = list() for id in resp['rgDispId']: IDs.append(id) return IDs def Invoke(self, dispIdMember, lcid, dwFlags, pDispParams, cVarRef, rgVarRefIdx, rgVarRef): request = IDispatch_Invoke() request['dispIdMember'] = dispIdMember request['riid'] = IID_NULL request['lcid'] = lcid request['dwFlags'] = dwFlags request['pDispParams'] = pDispParams request['cVarRef'] = cVarRef request['rgVarRefIdx'] = rgVarRefIdx request['rgVarRef'] = rgVarRefIdx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return resp