Олицетворение токена доступа с правами на резервное копирование и восстановление для копирования файлов по всему домену - PullRequest
0 голосов
/ 03 мая 2018
  • Компьютер A находится в Домене A
  • Компьютер B находится в Домене B
  • Домен A и домен B имеют доверие, позволяющее учетным записям подключаться к компьютерам другого домена
  • Учетная запись A является администратором на компьютере A и имеет права на резервное копирование и восстановление
  • Учетная запись B является администратором на компьютере B и имеет права на резервное копирование и восстановление
  • Учетная запись A НЕ является администратором на компьютере B и не имеет прав на резервное копирование и восстановление
  • Учетная запись B НЕ является администратором на компьютере A и не имеет прав на резервное копирование и восстановление

Лицо владеет как счетом А, так и счетом Б.
Они хотят скопировать файл с компьютера А на компьютер Б.
Они хотят скопировать этот файл из папки в папку, для которой у них нет разрешений. Следовательно, они оба должны включить полномочия резервного копирования и восстановления на своем токене доступа, который они получили от LogonUser

не разрешено изменять DACL для предоставления этих разрешений для любых папок или файлов, и вместо этого и Учетная запись A, и Учетная запись B должны включать привилегии B & R, которые распознаются компьютером, только тогда они будут возможность скопировать файл без отказа в доступе.


Проблема в том, что я пытался использовать NETWORK, INTERACTIVE и NEW_CREDENTIALS токены доступа и включить для них привилегии, но только NETWORK содержит привилегии, и они уже включены по умолчанию. Затем, когда я пытаюсь выполнить WindowsIdentity. Имитировать маркер NETWORK, а затем вызвать «CreateFile», чтобы открыть файл с привилегиями, он завершается неудачно и возвращает недопустимый дескриптор файла со значением -1. Я могу использовать INTERACTIVE для чтения неограниченного файла, но у него не было необходимых привилегий токена доступа (SeBackupPrivilege & SeRestorePrivilege), когда он был возвращен из LogonUser. Я полагаю, что когда Impersonate происходит, он генерирует токен, который «может» иметь эти привилегии, но я предполагаю, что он основан на машине, на которой выполняется код.

Существует ли способ олицетворения токена доступа -> включить права доступа B & R токена доступа на удаленных компьютерах, которые они обычно имели бы при работе на этом компьютере в качестве администратора, которые могли быть включены.

OR

Есть ли способ использовать токен NETWORK с олицетворением для успешного копирования файла с компьютера в домене A на компьютер в домене B. Если я запускаю программу под учетной записью B, которая не является администратором и пытается выдать себя за учетную запись A с сетью Похоже, учетные данные не работают на подражание

Ниже приведен демонстрационный код в консольном приложении, имитирующий ситуацию. Вы должны изменить части этого, чтобы проверить это соответственно: Вы должны создать пути к файлам и файл для чтения. Необходимо отредактировать разрешения для папки «DENYTHEIMPERSONATINGUSERHERE», чтобы учетная запись олицетворенного пользователя была отклонена и должна была использовать привилегии. Вы должны ввести действительные учетные данные, чтобы получить AccessToken.

class Program
{
static void Main(string[] args)
{
    //Credentials of Account A
    string usernameA = "AccountA";
    string DomainA = "DomainA.com";
    SecureString passwordA = new SecureString();
    passwordA.AppendChar('P');
    passwordA.AppendChar('W');
    passwordA.AppendChar('D');
    passwordA.AppendChar('A');


    IntPtr AccessToken = IntPtr.Zero;

    //Getting Network Access Token. (Network is an Impersonation Token, most other types are returned as Primary Tokens)
    AccessTokenHelper.LOGON32_LOGONERROR lgnCode = AccessTokenHelper.GetAccessToken(DomainA, usernameA, passwordA, AccessTokenHelper.LOGON32_LOGONTYPE.LOGON32_LOGON_NETWORK, AccessTokenHelper.LOGON32_LOGONPROVIDER.LOGON32_PROVIDER_WINNT50, out AccessToken);



    /*
        //Getting INTERACTIVE Access Token. (returns Primary Token)
        //Impersonation will work but won't have enabled privileges so will error when trying to write file.
        AccessTokenHelper.LOGON32_LOGONERROR lgnCode = AccessTokenHelper.GetAccessToken(DomainA, usernameA, passwordA, AccessTokenHelper.LOGON32_LOGONTYPE.LOGON32_LOGON_INTERACTIVE, AccessTokenHelper.LOGON32_LOGONPROVIDER.LOGON32_PROVIDER_WINNT50, out AccessToken);

    */


    if(AccessToken == IntPtr.Zero || AccessToken.ToInt32() == -1)
    {
        //Not valid creds
        System.Diagnostics.Debug.WriteLine(lgnCode.ToString());
        return;
    }


    //Enable Token Security Privileges
    AccessTokenHelper.ElevateSecurityPrivileges(AccessToken);

    //List Enabled Token Privileges
    List<string> tokenEnabledPrivileges = AccessTokenHelper.ListEnabledPrivileges(AccessToken);
    foreach(string s in tokenEnabledPrivileges)
    {
        System.Diagnostics.Debug.WriteLine(s);
    }

    string sourceFile = @"C:\Temp\Test1\TestData.txt";
    string destFile = @"C:\Temp\Test1\DENYTHEIMPERSONATINGUSERHERE\PrivCopy.txt";

    bool bCopied = PrivilegedEnterpriseCopyFile(sourceFile, destFile, AccessToken, AccessToken);
    System.Diagnostics.Debug.WriteLine("DID THE COPY WORK? " + bCopied.ToString().ToUpper());


}

[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern IntPtr CreateFile(
                                string lpFileName,
                                uint dwDesiredAccess,
                                OPENFILE_SHAREACCESS dwShareMode, //If you are reading folders you better have OPENFILE_SHAREACCESS.FILE_SHARE_READ
                                IntPtr SecurityAttributes,
                                uint dwCreationDisposition,
                                uint dwFlagsAndAttributes,
                                IntPtr hTemplateFile
                                );

private enum OPENFILE_SHAREACCESS : uint
{
    NO_SHARING = 0x00000000,
    FILE_SHARE_READ = 0x00000001,
    FILE_SHARE_WRITE = 0x00000002,
    FILE_SHARE_DELETE = 0x00000004
};

[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern bool CloseHandle(IntPtr hObject);

[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool GetFileSizeEx(IntPtr hFile, out long lpFileSize);

/// <summary>
/// Goal:   Enterprise Level Copy.... can copy file data between remote machines between domains.
/// </summary>
public static bool PrivilegedEnterpriseCopyFile(string sourceAbsoluteFileName, string destAbsoluteFileName, IntPtr sourceAccessToken, IntPtr destAccessToken, bool bOverwrite = false)
{
    const uint GENERIC_READ = 0x80000000;
    const uint GENERIC_WRITE = 0x40000000; //If you are writing to a file, best allow both READ and WRITE because Windows Performance suffers without.
    const uint CREATE_ALWAYS = 2; //Create and overwrite if necesarry
    const uint OPEN_EXISTING = 3; //Open only if exists
    const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; //This makes it take enabled Backup&Restore privileges into account
    const uint FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000; //This helps speed up reading.
    const uint FILE_FLAG_WRITE_THROUGH = 0x80000000;  //This helps speed up writing!
    //We pick a value that is the largest multiple of 4096 that is still smaller than the large object heap threshold (85K).
    // The CopyTo/CopyToAsync buffer is short-lived and is likely to be collected at Gen0, and it offers a significant
    // improvement in Copy performance.
    const int DefaultCopyBufferSize = 81920; //80kb

    bool bSuccess = false;

    //Get File Ptr Handles. DO NOT ALLOW DELETE ON SOURCE OR DEST WHILE THIS IS HAPPENING.
    //Get the file pointer by using an access token with access.
    IntPtr pSource = IntPtr.Zero;
    IntPtr pDest = IntPtr.Zero;

    //As source user, connect for optimized READING
    using (WindowsImpersonationContext impersonatedUser = WindowsIdentity.Impersonate(sourceAccessToken))
    {
        pSource = CreateFile(sourceAbsoluteFileName, GENERIC_READ, OPENFILE_SHAREACCESS.FILE_SHARE_READ,
                                IntPtr.Zero, OPEN_EXISTING,
                                FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN, IntPtr.Zero);
    }

    if (pSource == IntPtr.Zero || pSource.ToInt32() == -1)
    {
        //Failed to get Source, return false
        return bSuccess;
    }

    //Get Dest Path
    string DestPath = Path.GetDirectoryName(destAbsoluteFileName);
    string DestFileName = Path.GetFileName(destAbsoluteFileName);


    //As dest user
    using (WindowsImpersonationContext impersonatedUser = WindowsIdentity.Impersonate(destAccessToken))
    {
        try
        {
            bool bProceed = true;
            if (!bOverwrite)
            {
                //We don't want to overwrite existing file, ensure it doesn't exist.
                List<string> files = Directory.EnumerateFiles(DestPath).ToList();
                if (files.Any(s => s.Equals(DestFileName, StringComparison.OrdinalIgnoreCase)))
                {
                    //File exists, do not proceed
                    bProceed = false;
                }
            }
            //Do we proceed?
            if (bProceed)
            {
                //Create/Overwrite existing File
                pDest = CreateFile(destAbsoluteFileName, GENERIC_READ | GENERIC_WRITE, OPENFILE_SHAREACCESS.NO_SHARING,
                                    IntPtr.Zero, CREATE_ALWAYS,
                                    FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_WRITE_THROUGH, IntPtr.Zero);
            }
        }
        catch(Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex.Message);
        }
    }


    //If we successfully have both File Handles, we can proceed with Reading from source and writing to Dest.
    //If valid file pointers!!!
    if (pSource != IntPtr.Zero && pSource.ToInt32() != -1 &&
        pDest != IntPtr.Zero && pDest.ToInt32() != -1)
    {
        //Put Handle into more supported SafeFileHandle Ptr.
        SafeFileHandle safeSourcePtr = new SafeFileHandle(pSource, false); //We will close handle manually
        SafeFileHandle safeDestPtr = new SafeFileHandle(pDest, false);  //We will close handle manually

        try
        {
            using (FileStream fsSource = new FileStream(safeSourcePtr, FileAccess.Read, DefaultCopyBufferSize))
            {
                using (FileStream fsDest = new FileStream(safeDestPtr, FileAccess.ReadWrite, DefaultCopyBufferSize))
                {
                    //Here we read X bytes up to limit from fsSource and write to fsDest, until no more bytes!
                    // Read the source file into a byte array.

                    byte[] buffer = new byte[DefaultCopyBufferSize];
                    int bytesRead = -1;
                    while ((bytesRead = fsSource.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        fsDest.Write(buffer, 0, bytesRead);
                    }
                    //Force data to be flushed to harddisk.
                    fsDest.Flush(true);
                }
            }
        }
        catch { }

        //Compare File Size to know if we successfully wrote bytes to destination!
        //We'll assume it'd error out if it didn't.
        GetFileSizeEx(pSource, out long sourceSize);
        GetFileSizeEx(pDest, out long destSize);
        if (sourceSize == destSize)
        {
            //consider it a success
            bSuccess = true;
        }
    }

    if (pSource != IntPtr.Zero && pSource.ToInt32() != -1)
    {
        //Close file handle manually
        CloseHandle(pSource);
    }

    if (pDest != IntPtr.Zero && pDest.ToInt32() != -1)
    {
        //Close file handle manually
        CloseHandle(pDest);
    }

    return bSuccess;
}



}

public class AccessTokenHelper
{
public enum LOGON32_LOGONTYPE : int
{
    LOGON32_LOGON_INTERACTIVE = 2,
    LOGON32_LOGON_NETWORK = 3,
    LOGON32_LOGON_BATCH = 4,
    LOGON32_LOGON_SERVICE = 5,
    LOGON32_LOGON_UNLOCK = 7,
    LOGON32_LOGON_NETWORK_CLEARTEXT = 8,
    LOGON32_LOGON_NEW_CREDENTIALS = 9,
};

public enum LOGON32_LOGONPROVIDER : int
{
    LOGON32_PROVIDER_DEFAULT = 0,
    LOGON32_PROVIDER_WINNT40 = 2,
    LOGON32_PROVIDER_WINNT50 = 3,
};

public enum LOGON32_LOGONERROR : int
{
    ERROR_SUCCESS = 0,
    ERROR_NO_LOGON_SERVERS = 1311,
    ERROR_INVALID_ACCOUNT_NAME = 1315,
    ERROR_LOGON_FAILURE = 1326,
    ERROR_ACCOUNT_RESTRICTION = 1327,
    ERROR_INVALID_LOGON_HOURS = 1328,
    ERROR_INVALID_WORKSTATION_LOGONDENIED = 1329,
    ERROR_PASSWORD_EXPIRED = 1330,
    ERROR_ACCOUNT_DISABLED = 1331,
    ERROR_INVALID_LOGON_TYPE = 1367,
    ERROR_LOGON_NOT_GRANTED = 1380,
    ERROR_NETLOGON_NOT_STARTED = 1792,
    ERROR_ACCOUNT_EXPIRED = 1793,
    ERROR_PASSWORD_MUST_CHANGE = 1907,
    ERROR_ACCOUNT_LOCKED_OUT = 1909,
};

[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private extern static bool LogonUser(string username, string domain, IntPtr password, LOGON32_LOGONTYPE logonType, LOGON32_LOGONPROVIDER logonProvider, out IntPtr token);

/// <summary>
/// Attempts to create Access token from information given.
/// </summary>
public static LOGON32_LOGONERROR GetAccessToken(string domain, string username, SecureString securepassword, LOGON32_LOGONTYPE eLOGONTYPE, LOGON32_LOGONPROVIDER eLOGONPROVIDER, out IntPtr token)
{
    token = IntPtr.Zero;
    // Marshal the SecureString to unmanaged memory.  I hate doing this but it's currently most secure way offered by Microsoft to get Access Token from Credentials.
    IntPtr passwordPtr = Marshal.SecureStringToGlobalAllocUnicode(securepassword);

    bool bSuccess = LogonUser(username,
                                domain,
                                passwordPtr,
                                eLOGONTYPE,
                                eLOGONPROVIDER,
                                out token);
    //return the error code, useful if not successful.
    int errResult = Marshal.GetLastWin32Error();

    // Zero-out and free the unmanaged string reference.
    Marshal.ZeroFreeGlobalAllocUnicode(passwordPtr);

    return (LOGON32_LOGONERROR)errResult;
}






[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool LookupPrivilegeName(string lpSystemName, IntPtr lpLuid, StringBuilder lpName, ref int cchName);

[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool LookupPrivilegeDisplayName(string systemName, string privilegeName, StringBuilder displayName, ref int cchDisplayName, out uint languageId);

[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out long lpLuid);

[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool AdjustTokenPrivileges(
IntPtr TokenHandle,
[MarshalAs(UnmanagedType.Bool)]bool DisableAllPrivileges,
ref TOKEN_PRIVILEGES NewState,
uint prevStateBuffer,
IntPtr prevStateNA,
IntPtr prevBufferNA);

[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool GetTokenInformation(
IntPtr hToken,
uint TokenInformationClass,
IntPtr TokenInformation,
int TokenInformationLength,
out int ReturnLength);

[StructLayout(LayoutKind.Sequential)]
public struct LUID
{
    public uint LowPart;
    public uint HighPart;
};

[StructLayout(LayoutKind.Sequential)]
public struct LUID_AND_ATTRIBUTES
{
    public LUID Luid;
    public uint Attributes;
}

[StructLayout(LayoutKind.Sequential)]
public struct TOKEN_PRIVILEGES
{
    public int PrivilegeCount;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
    public LUID_AND_ATTRIBUTES[] Privileges;
};

[Flags]
public enum SE_PRIVILEGE_STATE : uint
{
    SE_PRIVILEGE_DISABLED = 0x00,
    SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x01,
    SE_PRIVILEGE_ENABLED = 0x02,
    SE_PRIVILEGE_REMOVED = 0x04,
    SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000
};

/// <summary>
/// Will elevate Backup and Restore Privileges of Access Token (be it Primary Type or Impersonation Type) if Access Token has Privilege
///    1) SeBackupPrivilege -- this grants read access regardless of DACL entries.
///    2) SeRestorePrivilege -- this grants write access regardless of DACL entries.
///    3) SeSecurityPrivilege -- this grants access to SACL for audits/security log.
/// </summary>
public static void ElevateSecurityPrivileges(IntPtr hToken, bool bEnabled = true)
{
    SE_PRIVILEGE_STATE privSTATE = bEnabled ? SE_PRIVILEGE_STATE.SE_PRIVILEGE_ENABLED : SE_PRIVILEGE_STATE.SE_PRIVILEGE_DISABLED;
    List<string> SecurityPrivNames = new List<string>() { "SeBackupPrivilege", "SeRestorePrivilege", "SeSecurityPrivilege" };
    if (hToken != IntPtr.Zero)
    {
        AdjustAccessTokenPrivileges(hToken, SecurityPrivNames, privSTATE);
    }
}

public static void AdjustAccessTokenPrivileges(IntPtr TokenHandle, List<string> PrivilegeNames, SE_PRIVILEGE_STATE eNewPrivilegeState, string remoteMachineName = null)
{
    if (TokenHandle != IntPtr.Zero && PrivilegeNames != null && PrivilegeNames.Count > 0)
    {
        DataTable privDT = GetAccessTokenPrivilegesAsDataTable(TokenHandle, remoteMachineName);
        if (privDT != null && privDT.Rows.Count > 0)
        {
            //If we have privileges, try to set state!
            foreach (string privName in PrivilegeNames)
            {
                DataRow row = privDT.Select(string.Format("[{0}]='{1}'", privDT.Columns[1].ColumnName, privName)).FirstOrDefault();
                if (row != null)
                {
                    UpdateExistingTokenPrivilege(TokenHandle, row.Field<LUID>(0), eNewPrivilegeState);
                }
            }
        }
    }
}

private static bool UpdateExistingTokenPrivilege(IntPtr tokenHandle, LUID privLuid, SE_PRIVILEGE_STATE eNewPrivilegeState)
{
    //Create our updated tokenPriv to send what privs we want updated.
    TOKEN_PRIVILEGES tokenPrivs = new TOKEN_PRIVILEGES();
    tokenPrivs.PrivilegeCount = 1;
    tokenPrivs.Privileges = new LUID_AND_ATTRIBUTES[] { new LUID_AND_ATTRIBUTES() { Luid = privLuid, Attributes = (uint)eNewPrivilegeState } };
    //Adjust Token Privilege!
    bool bSuccess = AdjustTokenPrivileges(tokenHandle, false, ref tokenPrivs, 0, IntPtr.Zero, IntPtr.Zero);
    //Return result of trying to adjust token privilege.
    return bSuccess;
}

public static DataTable GetAccessTokenPrivilegesAsDataTable(IntPtr TokenHandle, string remoteMachineName = null)
{
    TOKEN_PRIVILEGES tokenPrivData = GetAccessTokenPrivileges(TokenHandle);

    DataTable privDT = new DataTable();
    privDT.Columns.Add("PrivilegeLUID", typeof(LUID));
    privDT.Columns.Add("PrivilegeName");
    privDT.Columns.Add("PrivilegeState", typeof(SE_PRIVILEGE_STATE));

    foreach (LUID_AND_ATTRIBUTES privData in tokenPrivData.Privileges)
    {
        string PrivilegeName = LookupPrivilegeName(privData.Luid, remoteMachineName);
        if (!string.IsNullOrEmpty(PrivilegeName))
        {
            DataRow row = privDT.NewRow();
            row[0] = privData.Luid;
            row[1] = PrivilegeName;
            row[2] = (SE_PRIVILEGE_STATE)privData.Attributes;
            //Add Row
            privDT.Rows.Add(row);
        }
    }
    return privDT;
}

private static TOKEN_PRIVILEGES GetAccessTokenPrivileges(IntPtr TokenHandle)
{
    uint TOKEN_INFORMATION_CLASS_TokenPrivileges = 3;

    if (TokenHandle != IntPtr.Zero)
    {
        int nBufferSize = 0;
        bool TokenInfoResult;
        //First call is to get buffer size!
        TokenInfoResult = GetTokenInformation(TokenHandle, TOKEN_INFORMATION_CLASS_TokenPrivileges, IntPtr.Zero, nBufferSize, out nBufferSize);
        //Allocate Token Info correctly.
        IntPtr pTokenInfo = Marshal.AllocHGlobal(nBufferSize);
        //Get our Token Info Data
        TokenInfoResult = GetTokenInformation(TokenHandle, TOKEN_INFORMATION_CLASS_TokenPrivileges, pTokenInfo, nBufferSize, out nBufferSize);

        if (TokenInfoResult)
        {
            TOKEN_PRIVILEGES returnedPrivilegeSet = (TOKEN_PRIVILEGES)Marshal.PtrToStructure(pTokenInfo, typeof(TOKEN_PRIVILEGES));
            int PrivilegeCount = returnedPrivilegeSet.PrivilegeCount;

            //lets create the array we should have had returned in the first place
            LUID_AND_ATTRIBUTES[] AllPrivs = new LUID_AND_ATTRIBUTES[PrivilegeCount]; //initialize an array to the right size, including 0!
            if (PrivilegeCount > 0)
            {
                LUID_AND_ATTRIBUTES currentPriv = new LUID_AND_ATTRIBUTES();
                //pPrivileges will hold our new location to read from by taking the last pointer plus the size of the last structure read
                IntPtr pPrivilege = new IntPtr(pTokenInfo.ToInt32() + sizeof(int)); //pointer math, we point to the first element of Privileges array in the struct.
                currentPriv = (LUID_AND_ATTRIBUTES)Marshal.PtrToStructure(pPrivilege, typeof(LUID_AND_ATTRIBUTES)); //Get Privilege from pointer
                AllPrivs[0] = currentPriv; //We'll add the first element to our array.

                //After getting our first structure we can loop through the rest since they will all be the same
                for (int i = 1; i < PrivilegeCount; ++i)
                {
                    pPrivilege = new IntPtr(pPrivilege.ToInt32() + Marshal.SizeOf(currentPriv)); //This will point to the next Privilege element in the array
                    currentPriv = (LUID_AND_ATTRIBUTES)Marshal.PtrToStructure(pPrivilege, typeof(LUID_AND_ATTRIBUTES)); //Get Privilege from pointer
                    AllPrivs[i] = currentPriv; //Add element to the array
                }
            }
            //Create our complete struct of TOKEN_PRIVILEGES
            TOKEN_PRIVILEGES completePrivilegeSet = new TOKEN_PRIVILEGES();
            completePrivilegeSet.PrivilegeCount = PrivilegeCount;
            completePrivilegeSet.Privileges = AllPrivs;

            //We can get release all the pointers now, we got what we wanted!
            Marshal.FreeHGlobal(pTokenInfo);  //Free up the reserved space in unmanaged memory (Should be done any time AllocHGlobal is used)

            //Return our completePrivilegeSet!
            return completePrivilegeSet;
        }
    }
    return new TOKEN_PRIVILEGES() { PrivilegeCount = 0, Privileges = new LUID_AND_ATTRIBUTES[] { } };
}

private static string LookupPrivilegeName(LUID privLuid, string remoteMachineName = null)
{
    string PrivilegeName = null;
    StringBuilder sb = new StringBuilder();
    int cchName = 0; //Holds the length of structure we will be receiving LookupPrivilagename
    IntPtr ipLuid = Marshal.AllocHGlobal(Marshal.SizeOf(privLuid)); //Allocate a block of memory large enough to hold the structure
    Marshal.StructureToPtr(privLuid, ipLuid, true); //Write the structure into the reserved space in unmanaged memory
    LookupPrivilegeName(remoteMachineName, ipLuid, null, ref cchName); // call once to get the name length we will be receiving
    sb.Capacity = cchName; //Our string builder is buffered for the name!
    if (LookupPrivilegeName(remoteMachineName, ipLuid, sb, ref cchName))
    {
        // Successfully retrieved name!
        PrivilegeName = sb.ToString();
    }
    Marshal.FreeHGlobal(ipLuid); //Free up the reserved space in unmanaged memory
    return PrivilegeName;
}

public static List<string> ListEnabledPrivileges(IntPtr TokenHandle, string remoteMachineName = null)
{
    List<string> enabledPrivs = null;

    DataTable dt = GetAccessTokenPrivilegesAsDataTable(TokenHandle, remoteMachineName);

    if (dt != null && dt.Rows.Count > 0)
    {
        uint nEnabled = (uint)SE_PRIVILEGE_STATE.SE_PRIVILEGE_ENABLED;
        uint nEnabledAndDefault = (uint)(SE_PRIVILEGE_STATE.SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_STATE.SE_PRIVILEGE_ENABLED);



        string query = string.Format("[{0}] IN ( {1}, {2} )"
                                                        , dt.Columns[2].ColumnName
                                                        , nEnabled
                                                        , nEnabledAndDefault);
        IEnumerable<DataRow> rows = dt.Select(query);
        if (rows != null && rows.Count() > 0)
        {
            enabledPrivs = dt.Select(query).Select(r => r.Field<string>(1)).ToList();
        }

    }
    return enabledPrivs;
}

}

1 Ответ

0 голосов
/ 08 мая 2018

Итак, после долгих исследований выяснилось, что это заблокировано в Windows. Если текущий токен процесса не имеет SeTcbPrivilege (действует как часть операционной системы) и он не включен, вы не можете получить неограниченный / повышенный токен, когда решаете выдать себя за не повышенный токен.

Кроме того, если вы затем попытаетесь получить токен LinkedToken / Unrestricted токена доступа, но у вас не будет включен SeTcbPrivilege, вы получите токен с SECURITY_IMPERSONATION_LEVEL из SecurityIdentification, который не может использоваться для олицетворения . Если SECURITY_IMPERSONATION_LEVEL не является SecurityImpersonation (локальный доступ) или SecurityDelegation (локальный + удаленный доступ), вы не сможете выдать себя за маркер неограниченного доступа и привести к ошибке, так как LastError будет ERROR_BAD_IMPERSONATION_LEVEL (либо недопустимый уровень олицетворения недопустим, либо предоставленный недействительный имперсон ).

Это означает, что если у вас нет SeTcbPrivilege и он не включен на токене, он должен иметь маркер процесса с повышенными правами (например, Запуск от имени администратора использовался для ручного или принудительного запуска программы через уровень требуемого_элемента_ уровня = "requireAdministrator") ") тогда Windows не отменит привилегии на токене доступа, если вы решите выдать себя за токен доступа.

Используя учетную запись локального администратора, которая запускает программу от имени администратора, а затем с помощью учетных данных учетной записи домена, которые являются администратором на другом компьютере, для получения токена доступа, используя параметры LOGON32_LOGONTYPE.LOGON32_LOGON_NEW_CREDENTIALS и LOGON32_LOGONPROVIDER.LOGON32_PROVIDER_W. Затем можно повысить привилегии для токена NEWCRED, выдать себя за токен NEWCRED, который не будет лишать привилегий, и успешно подключить его к другому компьютеру, прочитать файл и записать в файл на другом компьютере, к которому у него нет разрешения или даже явно отказано, доказывая, что он использовал полномочия резервного копирования и восстановления токена!

Полезные источники:

...