Evolve_or_Die
Evolve_or_Die

Reputation: 119

What is the api to impersonate a Windows local user account without providing the password?

Let me rephrase my question according to Derek suggestion.

In Windows Vista/7/8/8.1 the task scheduler can execute a task within the security context of a local user account without storing the local account user password. This is the case when the task has both the options "Run whether user is logged on or not" and "Do not store password" selected. The only constraint is that the task, with the above options selected, can't access to encrypted files or to network resources.

I need to develop on a stand-alone machine an highly trusted application (executed as Local System) that executes some tasks within the security context of a local user account without storing the local account user password, the tasks don’t need access to network resources or to local encrypted files. Impersonation of the local user account already logged in is not an option, I need to run the application even when the system restarts and the user doesn’t login to the system.

Since the task scheduler can do what I need, this means that there must be an api that is able to create a local user access token without providing the user password. Does anyone know what this api is?

Any help you can give will be greatly appreciated.

P.s.: The machine is stand-alone, not domain joined.

Upvotes: 3

Views: 1288

Answers (1)

Evolve_or_Die
Evolve_or_Die

Reputation: 119

Better late than never. You estabilish a connection to the LSA server, you search for the "Negotiate" authentication package, you create an authentication info structure filling in as the type of logon the constant MsV1_0S4ULogon that identifies a service for user (S4U) logon. When an S4U logon is used, no password is stored by the system and there is no access to the network or encrypted files. The SeTcbPrivilege privilege is needed to be able to create the token for impersonation purposes. If you execute the program as administrator who, by default, doesn't have the SeTcbPrivilege privilege, the program returns success but the token created is not a primary token and can be used only for identification purposes.

#include "stdafx.h"
#include <Windows.h>
#include <Ntsecapi.h>
#include <stdio.h>
#include <Userenv.h>

size_t wcsByteLen(const wchar_t* str);
void InitUnicodeString(UNICODE_STRING& str, const wchar_t* value, BYTE* buffer, size_t& offset);
//#define NEGOSSP_NAME_A  "Negotiate"


int main(int argc, char * argv[])
{
    HANDLE lsa;
    ULONG Flags = MSV1_0_S4U_LOGON_FLAG_CHECK_LOGONHOURS;
    DWORD len;
    TOKEN_STATISTICS tokenstats;

    LsaConnectUntrusted(&lsa);

    const wchar_t* domain = L"Computer_name";
    const wchar_t* user = L"User_name";

    // prepare the authentication info
    ULONG authInfoSize = sizeof(MSV1_0_S4U_LOGON) + wcsByteLen(user) + wcsByteLen(domain);
    BYTE* authInfoBuf = new BYTE[authInfoSize];
    MSV1_0_S4U_LOGON* authInfo = (MSV1_0_S4U_LOGON*)authInfoBuf;
    // https://msdn.microsoft.com/en-us/aa378764
    authInfo->MessageType = MsV1_0S4ULogon;
    /*authInfo->Flags = Flags;*/
    size_t offset = sizeof(MSV1_0_S4U_LOGON);

    InitUnicodeString(authInfo->UserPrincipalName, user, authInfoBuf, offset);
    InitUnicodeString(authInfo->DomainName, domain, authInfoBuf, offset);

    // find the Negotiate security package
    char packageNameRaw[] = "Negotiate";
    LSA_STRING packageName;
    packageName.Buffer = packageNameRaw;
    packageName.Length = packageName.MaximumLength = (USHORT)strlen(packageName.Buffer);
    ULONG packageId;
    NTSTATUS stao_05 = LsaLookupAuthenticationPackage(lsa, &packageName, &packageId);

    // create a test origin and token source
    LSA_STRING origin = {};
    origin.Buffer = _strdup("Test");
    origin.Length = (USHORT)strlen(origin.Buffer);
    origin.MaximumLength = origin.Length;
    TOKEN_SOURCE source = {};
    strcpy_s(source.SourceName, "Test");
    bool test = AllocateLocallyUniqueId(&source.SourceIdentifier);

    void* profileBuffer;
    DWORD profileBufLen;
    LUID luid;
    HANDLE token;
    QUOTA_LIMITS qlimits;
    NTSTATUS subStatus;
    NTSTATUS status = LsaLogonUser(lsa, &origin, Batch, packageId, authInfo, authInfoSize, NULL, &source, &profileBuffer, &profileBufLen, &luid, &token, &qlimits, &subStatus);
    if (status == ERROR_SUCCESS)
    {
        if (GetTokenInformation(token, TokenStatistics, &tokenstats, sizeof(TOKEN_STATISTICS), &len) == TRUE)
        {
            switch (tokenstats.TokenType)
            {
                case TokenPrimary: printf("Token type: %s\n", "Primary");
                break;
                case TokenImpersonation: printf("Token type: %s\n", "Impersonation");
                break;
            }
            switch (tokenstats.ImpersonationLevel)
            {
                case SecurityAnonymous: printf("Impersonation level: %s\n", "Anonymous");
                break;
                case SecurityIdentification: printf("Impersonation level: %s\n", "Identification");
                break;
                case SecurityImpersonation: printf("Impersonation level: %s\n", "Impersonation");
                break;
                case SecurityDelegation: printf("Impersonation level: %s\n", "Delegation");
                break;
            }
        }
    }
    else
    {
        ULONG err = LsaNtStatusToWinError(status);
        printf("LsaLogonUser failed: %x\n", status);
        return 1;
    }

}

size_t wcsByteLen(const wchar_t* str)
{
    return wcslen(str) * sizeof(wchar_t);
}

void InitUnicodeString(UNICODE_STRING& str, const wchar_t* value, BYTE* buffer, size_t& offset)
{
    size_t size = wcsByteLen(value);
    str.Length = str.MaximumLength = (USHORT)size;
    str.Buffer = (PWSTR)(buffer + offset);
    memcpy(str.Buffer, value, size);
    offset += size;
}

Upvotes: 6

Related Questions