C# Using CredWrite to Access C$
Asked Answered
A

1

8

I'm trying to access the C$ of a server with a domain account that does not have right's to that server. I need to save the the credentials in the form of a local log in for that server for the program to work correctly.

How do I save those credentials using CredWrite

The Credential Manager class I've found: (EDIT: The following is functioning code.)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

namespace Test_Manager
{
    public class Win32CredMan
    {
        [DllImport("Advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
        static extern bool CredRead(string target, CRED_TYPE type, int reservedFlag, out IntPtr CredentialPtr);

        [DllImport("Advapi32.dll", EntryPoint = "CredWriteW", CharSet = CharSet.Unicode, SetLastError = true)]
        static extern bool CredWrite([In] ref NativeCredential userCredential, [In] UInt32 flags);

        [DllImport("Advapi32.dll", EntryPoint = "CredFree", SetLastError = true)]
        static extern bool CredFree([In] IntPtr cred);

        public enum CRED_TYPE : uint
        {
            GENERIC = 1,
            DOMAIN_PASSWORD = 2,
            DOMAIN_CERTIFICATE = 3,
            DOMAIN_VISIBLE_PASSWORD = 4,
            GENERIC_CERTIFICATE = 5,
            DOMAIN_EXTENDED = 6,
            MAXIMUM = 7,      // Maximum supported cred type
            MAXIMUM_EX = (MAXIMUM + 1000),  // Allow new applications to run on old OSes
        }
        public enum CRED_PERSIST : uint
        {
            SESSION = 1,
            LOCAL_MACHINE = 2,
            ENTERPRISE = 3,
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        private struct NativeCredential
        {
            public UInt32 Flags;
            public CRED_TYPE Type;
            public IntPtr TargetName;
            public IntPtr Comment;
            public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
            public UInt32 CredentialBlobSize;
            public IntPtr CredentialBlob;
            public UInt32 Persist;
            public UInt32 AttributeCount;
            public IntPtr Attributes;
            public IntPtr TargetAlias;
            public IntPtr UserName;

            /// <summary>
            /// This method derives a NativeCredential instance from a given Credential instance.
            /// </summary>
            /// <param name="cred">The managed Credential counterpart containing data to be stored.</param>
            /// <returns>A NativeCredential instance that is derived from the given Credential
            /// instance.</returns>
            internal static NativeCredential GetNativeCredential(Credential cred)
            {
                NativeCredential ncred = new NativeCredential();
                ncred.AttributeCount = 0;
                ncred.Attributes = IntPtr.Zero;
                ncred.Comment = IntPtr.Zero;
                ncred.TargetAlias = IntPtr.Zero;
                ncred.Type = (CRED_TYPE)cred.Type;
                ncred.Persist = (UInt32)cred.Persist;
                ncred.CredentialBlobSize = (UInt32)cred.CredentialBlobSize;
                ncred.TargetName = Marshal.StringToCoTaskMemUni(cred.TargetName);
                ncred.CredentialBlob = Marshal.StringToCoTaskMemUni(cred.CredentialBlob);
                ncred.UserName = Marshal.StringToCoTaskMemUni(cred.UserName);
                return ncred;
            }
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        private struct Credential
        {
            public UInt32 Flags;
            public CRED_TYPE Type;
            public string TargetName;
            public string Comment;
            public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
            public UInt32 CredentialBlobSize;
            public string CredentialBlob;
            public CRED_PERSIST Persist;
            public UInt32 AttributeCount;
            public IntPtr Attributes;
            public string TargetAlias;
            public string UserName;
        }

        #region Critical Handle Type definition
        sealed class CriticalCredentialHandle : CriticalHandleZeroOrMinusOneIsInvalid
        {
            // Set the handle.
            internal CriticalCredentialHandle(IntPtr preexistingHandle)
            {
                SetHandle(preexistingHandle);
            }

            internal Credential GetCredential()
            {
                if (!IsInvalid)
                {
                    // Get the Credential from the mem location
                    NativeCredential ncred = (NativeCredential)Marshal.PtrToStructure(handle,
                          typeof(NativeCredential));

                    // Create a managed Credential type and fill it with data from the native counterpart.
                    Credential cred = new Credential();
                    cred.CredentialBlobSize = ncred.CredentialBlobSize;
                    cred.CredentialBlob = Marshal.PtrToStringUni(ncred.CredentialBlob,
                          (int)ncred.CredentialBlobSize / 2);
                    cred.UserName = Marshal.PtrToStringUni(ncred.UserName);
                    cred.TargetName = Marshal.PtrToStringUni(ncred.TargetName);
                    cred.TargetAlias = Marshal.PtrToStringUni(ncred.TargetAlias);
                    cred.Type = ncred.Type;
                    cred.Flags = ncred.Flags;
                    cred.Persist = (CRED_PERSIST)ncred.Persist;
                    return cred;
                }
                else
                {
                    throw new InvalidOperationException("Invalid CriticalHandle!");
                }
            }

            // Perform any specific actions to release the handle in the ReleaseHandle method.
            // Often, you need to use Pinvoke to make a call into the Win32 API to release the 
            // handle. In this case, however, we can use the Marshal class to release the unmanaged memory.

            override protected bool ReleaseHandle()
            {
                // If the handle was set, free it. Return success.
                if (!IsInvalid)
                {
                    // NOTE: We should also ZERO out the memory allocated to the handle, before free'ing it
                    // so there are no traces of the sensitive data left in memory.
                    CredFree(handle);
                    // Mark the handle as invalid for future users.
                    SetHandleAsInvalid();
                    return true;
                }
                // Return false. 
                return false;
            }
        }
        #endregion

        public int WriteCred(string key, string user, string secret)
        {
            // Validations.

            byte[] byteArray = Encoding.Unicode.GetBytes(secret);
            if (byteArray.Length > 512)
                throw new ArgumentOutOfRangeException("The secret message has exceeded 512 bytes.");

            // Go ahead with what we have are stuff it into the CredMan structures.
            Credential cred = new Credential();
            cred.TargetName = key;
            cred.UserName = user;
            cred.CredentialBlob = secret;
            cred.CredentialBlobSize = (UInt32)Encoding.Unicode.GetBytes(secret).Length;
            cred.AttributeCount = 0;
            cred.Attributes = IntPtr.Zero;
            cred.Comment = null;
            cred.TargetAlias = null;
            cred.Type = CRED_TYPE.DOMAIN_PASSWORD;
            cred.Persist = CRED_PERSIST.ENTERPRISE;
            NativeCredential ncred = NativeCredential.GetNativeCredential(cred);
            // Write the info into the CredMan storage.
            bool written = CredWrite(ref ncred, 0);
            int lastError = Marshal.GetLastWin32Error();
            if (written)
            {
                return 0;
            }
            else
            {
                string message = string.Format("CredWrite failed with the error code {0}.", lastError);
                throw new Exception(message);
            }
        }

        public static string ReadCred(string key)
        {
            // Validations.

            IntPtr nCredPtr;
            string readPasswordText = null;

            // Make the API call using the P/Invoke signature
            bool read = CredRead(key, CRED_TYPE.GENERIC, 0, out nCredPtr);
            int lastError = Marshal.GetLastWin32Error();

            // If the API was successful then...
            if (read)
            {
                using (CriticalCredentialHandle critCred = new CriticalCredentialHandle(nCredPtr))
                {
                    Credential cred = critCred.GetCredential();
                    readPasswordText = cred.CredentialBlob;
                }
            }
            else
            {
                readPasswordText = string.Empty;

                //1168 is "element not found" -- ignore that one and return empty string:
                if (lastError != 1168)
                {
                    string message = string.Format("ReadCred failed with the error code {0}.", lastError);
                    throw new Exception(message);
                }
            }
            return readPasswordText;
        }
    }
}

Long story short this is the method in the above code I'm trying to use:

public int WriteCred(string key, string user, string secret)
{
    // Validations.

    byte[] byteArray = Encoding.Unicode.GetBytes(secret);
    if (byteArray.Length > 512)
        throw new ArgumentOutOfRangeException("The secret message has exceeded 512 bytes.");

    // Go ahead with what we have are stuff it into the CredMan structures.
    Credential cred = new Credential();
    cred.TargetName = key;
    cred.UserName = user;
    cred.CredentialBlob = secret;
    cred.CredentialBlobSize = (UInt32)Encoding.Unicode.GetBytes(secret).Length;
    cred.AttributeCount = 0;
    cred.Attributes = IntPtr.Zero;
    cred.Comment = null;
    cred.TargetAlias = null;
    cred.Type = CRED_TYPE.DOMAIN_PASSWORD;
    cred.Persist = CRED_PERSIST.ENTERPRISE;
    NativeCredential ncred = NativeCredential.GetNativeCredential(cred);
    // Write the info into the CredMan storage.
    bool written = CredWrite(ref ncred, 0);
    int lastError = Marshal.GetLastWin32Error();
    if (written)
    {
        return 0;
    }
    else
    {
        string message = string.Format("CredWrite failed with the error code {0}.", lastError);
        throw new Exception(message);
    }
}

And this what I'm doing in the body of the program:

Win32CredMan cm = new Win32CredMan();
cm.WriteCred("TheServer-18", @"TheServer-18\Administrator", "P4SSw0rD!");

I'm assuming I'm not getting access because the credential is not being added properly.

UPDATE:

The process I have laid out is adding a Generic Credential to Windows Credential Manager. However if is not using the username specifed in the WriteCred method. I don't understand why.

Asberry answered 16/10, 2013 at 17:42 Comment(1)
is the 512 limit necessary?Effulgent
A
2

The problem is resolved and the above code is now fully functional. The issue had to do with GetNativeCredential it was not using the values that were assigned to cred in the WriteCred method but rather some static ones that had been set up.

Also, the way I was inputting the information into the WriteCred method was incorrect. The code above has been fixed so that it is fully functioning.

Asberry answered 17/10, 2013 at 12:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.