3

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.

Evolve_or_Die
  • 119
  • 2
  • 9
  • Welcome to SO! Just a thought - as is the case with emails, the longer your question is, the less likely someone will read all the way through it. In fact, I think you can probably leave out all the background you provide up to and including the line, "So I come to my scenario." You may want to consider editing those paragraphs out, as I don't think you lose any context by not having them. And also put a code block around your code, which will make the overall text easier to read. – Derek Oct 31 '13 at 20:44
  • 1
    Derek, thanks for the comment. I rephrased the question – Evolve_or_Die Nov 01 '13 at 19:26
  • It might be the case that this is an internal API and/or not exposed to developers. After all, the kernel can do basically whatever it wants...I don't really have any guesses beyond that. – Michael Nov 01 '13 at 20:40
  • Michael,using the Sysinternals tool "Process monitor" to capture what's going on when a "S4U" logon type scheduled task runs,I discovered that the api "LsaApLogonUserEx2" is called.The api is contained in the standard MSV1_0.dll authentication package.So maybe the "LsaApLogonUserEx2" function is an undocumented api implemented by Microsoft to let the Local System use its "Act as part of the operating system" privilege that allows a process to impersonate any user without authentication. But, even assuming that I'm right, I don't have any idea on how I could call this api. – Evolve_or_Die Nov 01 '13 at 20:54
  • Same problem here. Did you find a solution? – ygoe Jun 09 '14 at 15:13

1 Answers1

6

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;
}
Evolve_or_Die
  • 119
  • 2
  • 9