using System; using System.Collections.Generic; using System.Runtime.InteropServices; namespace SharpDPAPI { /// /// Inspired by vletoux and gentilkiwi /// https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1 /// Mimikatz: kull_m_rpc_ms-bkrp_c.c /// public class Bkrp { #region pinvoke [DllImport("Rpcrt4.dll", EntryPoint = "RpcBindingFromStringBindingW", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = false)] private static extern Int32 RpcBindingFromStringBinding(String bindingString, out IntPtr lpBinding); [DllImport("Rpcrt4.dll", EntryPoint = "NdrClientCall2", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, SetLastError = false)] private static extern IntPtr NdrClientCall2(IntPtr pMIDL_STUB_DESC, IntPtr formatString, IntPtr h, IntPtr pguidActionAgent, IntPtr pDataIn, UInt32 cbDataIn, out IntPtr ppDataOut, out IntPtr pcbDataOut, UInt32 dwParam); [DllImport("Rpcrt4.dll", EntryPoint = "RpcBindingFree", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = false)] private static extern Int32 RpcBindingFree(ref IntPtr lpString); //#region RpcStringBindingCompose [DllImport("Rpcrt4.dll", EntryPoint = "RpcStringBindingComposeW", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = false)] private static extern Int32 RpcStringBindingCompose( String ObjUuid, String ProtSeq, String NetworkAddr, String Endpoint, String Options, out IntPtr lpBindingString ); [StructLayout(LayoutKind.Sequential)] private struct RPC_SECURITY_QOS { public Int32 Version; public Int32 Capabilities; public Int32 IdentityTracking; public Int32 ImpersonationType; }; [DllImport("Rpcrt4.dll", EntryPoint = "RpcBindingSetAuthInfoExW", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = false)] private static extern Int32 RpcBindingSetAuthInfoEx(IntPtr lpBinding, string ServerPrincName, UInt32 AuthnLevel, UInt32 AuthnSvc, IntPtr identity, UInt32 AuthzSvc, ref RPC_SECURITY_QOS SecurityQOS); [DllImport("Rpcrt4.dll", EntryPoint = "RpcBindingSetOption", CallingConvention = CallingConvention.StdCall, SetLastError = false)] private static extern Int32 RpcBindingSetOption(IntPtr Binding, UInt32 Option, IntPtr OptionValue); [DllImport("Rpcrt4.dll", EntryPoint = "I_RpcBindingInqSecurityContext", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)] private static extern Int32 I_RpcBindingInqSecurityContext(IntPtr Binding, out IntPtr SecurityContextHandle); [StructLayout(LayoutKind.Sequential)] private struct SecPkgContext_SessionKey { public UInt32 SessionKeyLength; public IntPtr SessionKey; } [DllImport("secur32.Dll", CharSet = CharSet.Auto, SetLastError = false)] private static extern int QueryContextAttributes(IntPtr hContext, uint ulAttribute, ref SecPkgContext_SessionKey pContextAttributes); [StructLayout(LayoutKind.Sequential)] private struct CRYPTO_BUFFER { public UInt32 Length; public UInt32 MaximumLength; public IntPtr Buffer; } [DllImport("advapi32.Dll", CharSet = CharSet.Auto, SetLastError = false, EntryPoint = "SystemFunction032")] private static extern int SystemFunction032(ref CRYPTO_BUFFER data, ref CRYPTO_BUFFER key); [DllImport("advapi32.dll", SetLastError = true, EntryPoint = "SystemFunction027")] private static extern int RtlDecryptDES2blocks1DWORD(byte[] data, ref UInt32 key, IntPtr output); [DllImport("advapi32.dll", SetLastError = true)] private static extern IntPtr GetSidSubAuthority(IntPtr sid, UInt32 subAuthorityIndex); [DllImport("advapi32.dll", SetLastError = true)] private static extern IntPtr GetSidSubAuthorityCount(IntPtr psid); #endregion #region rpc initialization private byte[] MIDL_ProcFormatString; private byte[] MIDL_TypeFormatString; private GCHandle procString; private GCHandle formatString; private GCHandle stub; private GCHandle faultoffsets; private GCHandle clientinterface; public UInt32 RPCTimeOut = 1000; [StructLayout(LayoutKind.Sequential)] private struct COMM_FAULT_OFFSETS { public short CommOffset; public short FaultOffset; } [StructLayout(LayoutKind.Sequential)] private struct GENERIC_BINDING_ROUTINE_PAIR { public IntPtr Bind; public IntPtr Unbind; } [StructLayout(LayoutKind.Sequential)] private struct RPC_VERSION { public ushort MajorVersion; public ushort MinorVersion; public RPC_VERSION(ushort InterfaceVersionMajor, ushort InterfaceVersionMinor) { MajorVersion = InterfaceVersionMajor; MinorVersion = InterfaceVersionMinor; } } [StructLayout(LayoutKind.Sequential)] private struct RPC_SYNTAX_IDENTIFIER { public Guid SyntaxGUID; public RPC_VERSION SyntaxVersion; } [StructLayout(LayoutKind.Sequential)] private struct RPC_CLIENT_INTERFACE { public uint Length; public RPC_SYNTAX_IDENTIFIER InterfaceId; public RPC_SYNTAX_IDENTIFIER TransferSyntax; public IntPtr /*PRPC_DISPATCH_TABLE*/ DispatchTable; public uint RpcProtseqEndpointCount; public IntPtr /*PRPC_PROTSEQ_ENDPOINT*/ RpcProtseqEndpoint; public IntPtr Reserved; public IntPtr InterpreterInfo; public uint Flags; public static readonly Guid IID_SYNTAX = new Guid(0x8A885D04u, 0x1CEB, 0x11C9, 0x9F, 0xE8, 0x08, 0x00, 0x2B, 0x10, 0x48, 0x60); public RPC_CLIENT_INTERFACE(Guid iid, ushort InterfaceVersionMajor, ushort InterfaceVersionMinor) { Length = (uint)Marshal.SizeOf(typeof(RPC_CLIENT_INTERFACE)); RPC_VERSION rpcVersion = new RPC_VERSION(InterfaceVersionMajor, InterfaceVersionMinor); InterfaceId = new RPC_SYNTAX_IDENTIFIER(); InterfaceId.SyntaxGUID = iid; InterfaceId.SyntaxVersion = rpcVersion; rpcVersion = new RPC_VERSION(2, 0); TransferSyntax = new RPC_SYNTAX_IDENTIFIER(); TransferSyntax.SyntaxGUID = IID_SYNTAX; TransferSyntax.SyntaxVersion = rpcVersion; DispatchTable = IntPtr.Zero; RpcProtseqEndpointCount = 0u; RpcProtseqEndpoint = IntPtr.Zero; Reserved = IntPtr.Zero; InterpreterInfo = IntPtr.Zero; Flags = 0u; } } [StructLayout(LayoutKind.Sequential)] private struct MIDL_STUB_DESC { public IntPtr /*RPC_CLIENT_INTERFACE*/ RpcInterfaceInformation; public IntPtr pfnAllocate; public IntPtr pfnFree; public IntPtr pAutoBindHandle; public IntPtr /*NDR_RUNDOWN*/ apfnNdrRundownRoutines; public IntPtr /*GENERIC_BINDING_ROUTINE_PAIR*/ aGenericBindingRoutinePairs; public IntPtr /*EXPR_EVAL*/ apfnExprEval; public IntPtr /*XMIT_ROUTINE_QUINTUPLE*/ aXmitQuintuple; public IntPtr pFormatTypes; public int fCheckBounds; /* Ndr library version. */ public uint Version; public IntPtr /*MALLOC_FREE_STRUCT*/ pMallocFreeStruct; public int MIDLVersion; public IntPtr CommFaultOffsets; // New fields for version 3.0+ public IntPtr /*USER_MARSHAL_ROUTINE_QUADRUPLE*/ aUserMarshalQuadruple; // Notify routines - added for NT5, MIDL 5.0 public IntPtr /*NDR_NOTIFY_ROUTINE*/ NotifyRoutineTable; public IntPtr mFlags; // International support routines - added for 64bit post NT5 public IntPtr /*NDR_CS_ROUTINES*/ CsRoutineTables; public IntPtr ProxyServerInfo; public IntPtr /*NDR_EXPR_DESC*/ pExprInfo; // Fields up to now present in win2000 release. public MIDL_STUB_DESC(IntPtr pFormatTypesPtr, IntPtr RpcInterfaceInformationPtr, IntPtr pfnAllocatePtr, IntPtr pfnFreePtr, IntPtr aGenericBindingRoutinePairsPtr) { pFormatTypes = pFormatTypesPtr; RpcInterfaceInformation = RpcInterfaceInformationPtr; CommFaultOffsets = IntPtr.Zero; pfnAllocate = pfnAllocatePtr; pfnFree = pfnFreePtr; pAutoBindHandle = IntPtr.Zero; apfnNdrRundownRoutines = IntPtr.Zero; aGenericBindingRoutinePairs = aGenericBindingRoutinePairsPtr; apfnExprEval = IntPtr.Zero; aXmitQuintuple = IntPtr.Zero; fCheckBounds = 1; Version = 0x60000; pMallocFreeStruct = IntPtr.Zero; MIDLVersion = 0x8000253; aUserMarshalQuadruple = IntPtr.Zero; NotifyRoutineTable = IntPtr.Zero; mFlags = new IntPtr(0x00000001); CsRoutineTables = IntPtr.Zero; ProxyServerInfo = IntPtr.Zero; pExprInfo = IntPtr.Zero; } } private void InitializeStub(Guid interfaceID, byte[] MIDL_ProcFormatString, byte[] MIDL_TypeFormatString, ushort MajorVerson, ushort MinorVersion) { this.MIDL_ProcFormatString = MIDL_ProcFormatString; this.MIDL_TypeFormatString = MIDL_TypeFormatString; procString = GCHandle.Alloc(this.MIDL_ProcFormatString, GCHandleType.Pinned); RPC_CLIENT_INTERFACE clientinterfaceObject = new RPC_CLIENT_INTERFACE(interfaceID, MajorVerson, MinorVersion); COMM_FAULT_OFFSETS commFaultOffset = new COMM_FAULT_OFFSETS(); commFaultOffset.CommOffset = -1; commFaultOffset.FaultOffset = -1; faultoffsets = GCHandle.Alloc(commFaultOffset, GCHandleType.Pinned); clientinterface = GCHandle.Alloc(clientinterfaceObject, GCHandleType.Pinned); formatString = GCHandle.Alloc(MIDL_TypeFormatString, GCHandleType.Pinned); MIDL_STUB_DESC stubObject = new MIDL_STUB_DESC(formatString.AddrOfPinnedObject(), clientinterface.AddrOfPinnedObject(), Marshal.GetFunctionPointerForDelegate((allocmemory)AllocateMemory), Marshal.GetFunctionPointerForDelegate((freememory)FreeMemory), IntPtr.Zero); stub = GCHandle.Alloc(stubObject, GCHandleType.Pinned); } private void freeStub() { procString.Free(); faultoffsets.Free(); clientinterface.Free(); formatString.Free(); stub.Free(); } private static List TrackedMemoryAllocations; delegate IntPtr allocmemory(int size); private static IntPtr AllocateMemory(int size) { IntPtr memory = Marshal.AllocHGlobal(size); if (TrackedMemoryAllocations != null) { TrackedMemoryAllocations.Add(memory); } return memory; } delegate void freememory(IntPtr memory); private static void FreeMemory(IntPtr memory) { Marshal.FreeHGlobal(memory); if (TrackedMemoryAllocations != null && TrackedMemoryAllocations.Contains(memory)) { TrackedMemoryAllocations.Remove(memory); } } private static void EnableMemoryTracking() { TrackedMemoryAllocations = new List(); } private static void FreeTrackedMemoryAndRemoveTracking() { List list = TrackedMemoryAllocations; TrackedMemoryAllocations = null; foreach (IntPtr memory in list) { Marshal.FreeHGlobal(memory); } } private IntPtr Bind(string server) { IntPtr bindingstring = IntPtr.Zero; IntPtr binding = IntPtr.Zero; Int32 status; status = RpcStringBindingCompose(null, "ncacn_np", server, @"\pipe\protected_storage", null, out bindingstring); if (status != 0) return IntPtr.Zero; status = RpcBindingFromStringBinding(Marshal.PtrToStringUni(bindingstring), out binding); RpcBindingFree(ref bindingstring); if (status != 0) return IntPtr.Zero; RPC_SECURITY_QOS qos = new RPC_SECURITY_QOS(); qos.Version = 1; qos.Capabilities = 8; qos.ImpersonationType = 3; GCHandle qoshandle = GCHandle.Alloc(qos, GCHandleType.Pinned); status = RpcBindingSetAuthInfoEx(binding, "ProtectedStorage/" + server, 6, 9, IntPtr.Zero, 0, ref qos); qoshandle.Free(); if (status != 0) { Unbind(binding); return IntPtr.Zero; } return binding; } private static void Unbind(IntPtr hBinding) { RpcBindingFree(ref hBinding); } private IntPtr GetProcStringHandle(int offset) { return Marshal.UnsafeAddrOfPinnedArrayElement(MIDL_ProcFormatStringBackuprKeyx64, offset); } private IntPtr GetStubHandle() { return stub.AddrOfPinnedObject(); } #endregion #region MIDL strings private static byte[] MIDL_ProcFormatStringBackuprKeyx64 = new byte[] { 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x32, 0x00, 0x00, 0x00, 0x54, 0x00, 0x24, 0x00, 0x47, 0x07, 0x0a, 0x07, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x08, 0x00, 0x0c, 0x00, 0x0b, 0x01, 0x10, 0x00, 0x1c, 0x00, 0x48, 0x00, 0x18, 0x00, 0x08, 0x00, 0x13, 0x20, 0x20, 0x00, 0x28, 0x00, 0x50, 0x21, 0x28, 0x00, 0x08, 0x00, 0x48, 0x00, 0x30, 0x00, 0x08, 0x00, 0x70, 0x00, 0x38, 0x00, 0x08, 0x00, 0x00 }; private static byte[] MIDL_TypeFormatStringBackuprKeyx64 = new byte[] { 0x00, 0x00, 0x11, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x08, 0x00, 0x01, 0x5b, 0x15, 0x03, 0x10, 0x00, 0x08, 0x06, 0x06, 0x4c, 0x00, 0xf1, 0xff, 0x5b, 0x11, 0x00, 0x02, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x29, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x5b, 0x11, 0x14, 0x02, 0x00, 0x12, 0x00, 0x02, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x29, 0x54, 0x28, 0x00, 0x00, 0x00, 0x01, 0x5b, 0x11, 0x0c, 0x08, 0x5c, 0x00 }; private static byte[] MIDL_ProcFormatStringBackuprKeyx86 = new byte[] { 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x32, 0x00, 0x00, 0x00, 0x54, 0x00, 0x24, 0x00, 0x47, 0x07, 0x08, 0x07, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x04, 0x00, 0x0c, 0x00, 0x0b, 0x01, 0x08, 0x00, 0x1c, 0x00, 0x48, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x13, 0x20, 0x10, 0x00, 0x28, 0x00, 0x50, 0x21, 0x14, 0x00, 0x08, 0x00, 0x48, 0x00, 0x18, 0x00, 0x08, 0x00, 0x70, 0x00, 0x1c, 0x00, 0x08, 0x00, 0x00 }; private static byte[] MIDL_TypeFormatStringBackuprKeyx86 = new byte[] { 0x00, 0x00, 0x11, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x08, 0x00, 0x01, 0x5b, 0x15, 0x03, 0x10, 0x00, 0x08, 0x06, 0x06, 0x4c, 0x00, 0xf1, 0xff, 0x5b, 0x11, 0x00, 0x02, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x29, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x5b, 0x11, 0x14, 0x02, 0x00, 0x12, 0x00, 0x02, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x29, 0x54, 0x14, 0x00, 0x00, 0x00, 0x01, 0x5b, 0x11, 0x0c, 0x08, 0x5c, 0x00 }; #endregion #region RPC structures [StructLayout(LayoutKind.Sequential)] private struct DRS_EXTENSIONS_INT { public UInt32 cb; public UInt32 dwFlags; public Guid SiteObjGuid; public UInt32 Pid; public UInt32 dwReplEpoch; public UInt32 dwFlagsExt; public Guid ConfigObjGUID; public UInt32 dwExtCaps; } [StructLayout(LayoutKind.Sequential)] private struct USN_VECTOR { public long usnHighObjUpdate; public long usnReserved; public long usnHighPropUpdate; } [StructLayout(LayoutKind.Sequential)] private struct SCHEMA_PREFIX_TABLE { public UInt32 PrefixCount; public IntPtr pPrefixEntry; } [StructLayout(LayoutKind.Sequential)] private struct ATTRVALBLOCK { public UInt32 valCount; public IntPtr pAVal; } [StructLayout(LayoutKind.Sequential)] private struct ATTRBLOCK { public UInt32 attrCount; public IntPtr pAttr; } [StructLayout(LayoutKind.Sequential)] private struct ENTINF { public IntPtr pName; public UInt32 ulFlags; public ATTRBLOCK AttrBlock; }; #endregion #region Bkrp class and public interfaces private IntPtr hBind; private Guid BACKUPKEY_RESTORE_GUID = new Guid("47270C64-2FC7-499B-AC5B-0E37CDCE899A"); private Guid MS_BKRP_INTERFACE_ID = new Guid("3DDE7C30-165D-11D1-AB8F-00805F14DB40"); public Bkrp() { if (IntPtr.Size == 8) { InitializeStub(this.MS_BKRP_INTERFACE_ID, MIDL_ProcFormatStringBackuprKeyx64, MIDL_TypeFormatStringBackuprKeyx64, 1, 0); } else { InitializeStub(this.MS_BKRP_INTERFACE_ID, MIDL_ProcFormatStringBackuprKeyx86, MIDL_TypeFormatStringBackuprKeyx86, 1, 0); } } ~Bkrp() { Uninitialize(); } public void Initialize(string server, string domain) { try { this.hBind = Bind(server); if (this.hBind == IntPtr.Zero) throw new Exception("Unable to connect to the server " + server); } catch (Exception) { if (this.hBind != IntPtr.Zero) Unbind(this.hBind); this.hBind = IntPtr.Zero; } } private void Uninitialize() { if (hBind != IntPtr.Zero) Unbind(hBind); } #endregion #region drsr rpc functions and decoding functions public byte[] BackuprKey(byte[] domainKeyBytes) { EnableMemoryTracking(); IntPtr result = IntPtr.Zero; var stub = GetStubHandle(); var handle = GetProcStringHandle(0); GCHandle guidHandle = GCHandle.Alloc(this.BACKUPKEY_RESTORE_GUID, GCHandleType.Pinned); IntPtr guidPtr = guidHandle.AddrOfPinnedObject(); GCHandle domainKeyHandle = GCHandle.Alloc(domainKeyBytes, GCHandleType.Pinned); IntPtr domainKeyPtr = domainKeyHandle.AddrOfPinnedObject(); IntPtr ppDataOut = IntPtr.Zero; IntPtr pcbDataOut = IntPtr.Zero; uint domainKeyLength = Convert.ToUInt32(domainKeyBytes.Length); uint dwParams = Convert.ToUInt32(0); try { result = NdrClientCall2(stub, handle, this.hBind, guidPtr, domainKeyPtr, domainKeyLength, out ppDataOut, out pcbDataOut, dwParams); byte[] managedArray = new byte[64]; IntPtr ptr = new IntPtr(ppDataOut.ToInt64() + 4); Marshal.Copy(ptr, managedArray, 0, 64); return managedArray; } catch (Exception) { throw new Exception("Error decrypting masterkey via RPC"); } finally { guidHandle.Free(); domainKeyHandle.Free(); this.freeStub(); FreeTrackedMemoryAndRemoveTracking(); } } #endregion } }