NTSTATUS OpenPolicy( LPWSTR ServerName, DWORD DesiredAccess, PLSA_HANDLE PolicyHandle ) { LSA_OBJECT_ATTRIBUTES ObjectAttributes; LSA_UNICODE_STRING ServerString; PLSA_UNICODE_STRING Server = NULL; // // Always initialize the object attributes to all zeroes. // ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes)); if (ServerName != NULL) { // // Make a LSA_UNICODE_STRING out of the LPWSTR passed in // InitLsaString(&ServerString, ServerName); Server = &ServerString; } // // Attempt to open the policy. // return myLsaOpenPolicy( Server, &ObjectAttributes, DesiredAccess, PolicyHandle ); }
NTSTATUS SetPrivilegeOnAccount( LSA_HANDLE PolicyHandle, // open policy handle PSID AccountSid, // SID to grant privilege to LPWSTR PrivilegeName, // privilege to grant (Unicode) BOOL bEnable // enable or disable ) { LSA_UNICODE_STRING PrivilegeString; // // Create a LSA_UNICODE_STRING for the privilege name. // InitLsaString(&PrivilegeString, PrivilegeName); // // grant or revoke the privilege, accordingly // // THIS IS ALWAYS TRUE IN OUR CASE return myLsaAddAccountRights( PolicyHandle, // open policy handle AccountSid, // target SID &PrivilegeString, // privileges 1 // privilege count ); }
NTSTATUS SetPrivilegeOnAccount(LSA_HANDLE PolicyHandle, PSID AccountSid, LPWSTR PrivilegeName, BOOL bEnable) { LSA_UNICODE_STRING PrivilegeString; InitLsaString(&PrivilegeString, PrivilegeName); return (bEnable)?(fLsaAddAccountRights(PolicyHandle,AccountSid,&PrivilegeString,1)) :(fLsaRemoveAccountRights(PolicyHandle,AccountSid,FALSE,&PrivilegeString,1)); }
LSA_HANDLE OpenPolicy(DWORD accessMask) { LSA_OBJECT_ATTRIBUTES objectAttributes; ZeroMemory(&objectAttributes, sizeof(objectAttributes)); LSA_UNICODE_STRING lsaMachineName; InitLsaString(&lsaMachineName, L"."); LSA_HANDLE hPolicy = NULL; CheckRetVal(LsaOpenPolicy(&lsaMachineName, &objectAttributes, accessMask, &hPolicy)); return hPolicy; }
void RevokePrivilege(PSID sid, LPCWSTR userRight) { LSA_HANDLE hPolicy = OpenPolicy(POLICY_LOOKUP_NAMES); try { LSA_UNICODE_STRING lsaUserRight; InitLsaString(&lsaUserRight, (LPWSTR)userRight); CheckRetVal(LsaRemoveAccountRights(hPolicy, sid, FALSE, &lsaUserRight, 1)); LsaClose(hPolicy); } catch (const std::exception&) { LsaClose(hPolicy); throw; } }
NTSTATUS SetPrivilegeOnAccount(LSA_HANDLE PolicyHandle, PSID AccountSid, LPWSTR PrivilegeName, BOOL bEnable) { LSA_UNICODE_STRING PrivilegeString; /* Create a LSA_UNICODE_STRING for the privilege name. */ InitLsaString(&PrivilegeString, PrivilegeName); /* grant or revoke the privilege, accordingly */ if (bEnable) return (LsaAddAccountRights(PolicyHandle, AccountSid, &PrivilegeString, 1)); else return (LsaRemoveAccountRights(PolicyHandle, AccountSid, FALSE, &PrivilegeString, 1)); }
void GrantPrivilege(PSID sid, LPCWSTR userRight) { LSA_HANDLE hPolicy = OpenPolicy(POLICY_LOOKUP_NAMES | POLICY_CREATE_ACCOUNT); try { LSA_UNICODE_STRING lsaUserRight; InitLsaString(&lsaUserRight, (LPWSTR)userRight); CheckRetVal(LsaAddAccountRights(hPolicy, sid, &lsaUserRight, 1)); LsaClose(hPolicy); } catch (const std::exception&) { LsaClose(hPolicy); throw; } }
DWORD AddPrivilegeToAccount(LSA_HANDLE PolicyHandle, LPWSTR AccountName, LPWSTR PrivilegeName) { DWORD total = 0; PLSA_TRANSLATED_SID2 ltsTranslatedSID2; PLSA_REFERENCED_DOMAIN_LIST lrdlDomainList; NTSTATUS ntsResult; LSA_UNICODE_STRING lucName; InitLsaString(&lucName, AccountName); if ((ntsResult = fLsaLookupNames2(PolicyHandle,0,1,&lucName,&lrdlDomainList,<sTranslatedSID2)) == ERROR_SUCCESS) { if (SetPrivilegeOnAccount(PolicyHandle,ltsTranslatedSID2->Sid,PrivilegeName, TRUE) == ERROR_SUCCESS) total++; } fLsaFreeMemory(ltsTranslatedSID2); fLsaFreeMemory(lrdlDomainList); return (total); }
DWORD SearchForPrivilegedAccounts(LPWSTR PrivilegeName, BOOL bEnable) { DWORD count = 0, total = 0; static SECURITY_QUALITY_OF_SERVICE sqos = {sizeof SECURITY_QUALITY_OF_SERVICE, SecurityImpersonation, SECURITY_DYNAMIC_TRACKING, FALSE}; static LSA_OBJECT_ATTRIBUTES lsaOA = {sizeof LSA_OBJECT_ATTRIBUTES, NULL, NULL, 0, NULL, &sqos}; LSA_HANDLE PolicyHandle; NTSTATUS nts; if ((nts = fLsaOpenPolicy(NULL, &lsaOA, GENERIC_ALL | POLICY_VIEW_LOCAL_INFORMATION | POLICY_LOOKUP_NAMES, &PolicyHandle)) == ERROR_SUCCESS) { if (bEnable) { for (DWORD i=0; i < sizeof(AccountName) / sizeof(LPWSTR); i++) total += AddPrivilegeToAccount(PolicyHandle, AccountName[i],PrivilegeName); } else { void *buffer; LSA_UNICODE_STRING PrivilegeString; InitLsaString(&PrivilegeString, PrivilegeName); if ((nts = fLsaEnumerateAccountsWithUserRight(PolicyHandle, &PrivilegeString, &buffer, &count)) == ERROR_SUCCESS) { SID **psidArray = (SID **)buffer; for (DWORD i = 0; i < count; ++ i) { if (SetPrivilegeOnAccount(PolicyHandle, psidArray[i], PrivilegeName, FALSE) == ERROR_SUCCESS) total++; } if (buffer) fLsaFreeMemory(buffer); buffer = NULL; } else addlog("[SECURE]: Failed to enumerate LSA accounts."); } fLsaClose(PolicyHandle); } else addlog("[SECURE]: Failed to open LSA system policy."); return (total); }
NTSTATUS SetPrivilegeOnAccount( LSA_HANDLE PolicyHandle, // open policy handle PSID AccountSid, // SID to grant privilege to LPWSTR PrivilegeName, // privilege to grant (Unicode) BOOL bEnable // enable or disable ) { LSA_UNICODE_STRING PrivilegeString; // // Create a LSA_UNICODE_STRING for the privilege name. // InitLsaString(&PrivilegeString, PrivilegeName); // // grant or revoke the privilege, accordingly // if(bEnable) { return LsaAddAccountRights( PolicyHandle, // open policy handle AccountSid, // target SID &PrivilegeString, // privileges 1 // privilege count ); } else { return LsaRemoveAccountRights( PolicyHandle, // open policy handle AccountSid, // target SID FALSE, // do not disable all rights &PrivilegeString, // privileges 1 // privilege count ); } }
int _tmain(int argc, TCHAR *argv[]) { BOOL bResult; NTSTATUS Status; NTSTATUS SubStatus; HANDLE hLsa = NULL; HANDLE hProcess = NULL; HANDLE hToken = NULL; HANDLE hTokenS4U = NULL; LSA_STRING Msv1_0Name = { 0 }; LSA_STRING OriginName = { 0 }; PMSV1_0_S4U_LOGON pS4uLogon = NULL; TOKEN_SOURCE TokenSource; ULONG ulAuthenticationPackage; DWORD dwMessageLength; PBYTE pbPosition; PROCESS_INFORMATION pi = { 0 }; STARTUPINFO si = { 0 }; PTOKEN_GROUPS pGroups = NULL; PSID pLogonSid = NULL; PSID pExtraSid = NULL; PVOID pvProfile = NULL; DWORD dwProfile = 0; LUID logonId = { 0 }; QUOTA_LIMITS quotaLimits; LPTSTR szCommandLine = NULL; LPTSTR szSrcCommandLine = TEXT("%systemroot%\\system32\\cmd.exe"); LPTSTR szDomain = NULL; LPTSTR szUsername = NULL; TCHAR seps[] = TEXT("\\"); TCHAR *next_token = NULL; g_hHeap = GetProcessHeap(); if (argc < 2) { fprintf(stderr, "Usage:\n s4u.exe Domain\\Username [Extra SID]\n\n"); goto End; } // // Get DOMAIN and USERNAME from command line. // szDomain = _tcstok_s(argv[1], seps, &next_token); if (szDomain == NULL) { fprintf(stderr, "Unable to parse command line.\n"); goto End; } szUsername = _tcstok_s(NULL, seps, &next_token); if (szUsername == NULL) { fprintf(stderr, "Unable to parse command line.\n"); goto End; } // // Activate the TCB privilege // hProcess = GetCurrentProcess(); OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken); if (!SetPrivilege(hToken, SE_TCB_NAME, TRUE)) { goto End; } // // Get logon SID // if (!GetLogonSID(hToken, &pLogonSid)) { fprintf(stderr, "Unable to find logon SID.\n"); goto End; } // // Connect (Untrusted) to LSA // Status = LsaConnectUntrusted(&hLsa); if (Status!=STATUS_SUCCESS) { fprintf(stderr, "LsaConnectUntrusted failed (error 0x%x).", Status); hLsa = NULL; goto End; } // // Lookup for the MSV1_0 authentication package (NTLMSSP) // InitLsaString(&Msv1_0Name, MSV1_0_PACKAGE_NAME); Status = LsaLookupAuthenticationPackage(hLsa, &Msv1_0Name, &ulAuthenticationPackage); if (Status!=STATUS_SUCCESS) { fprintf(stderr, "LsaLookupAuthenticationPackage failed (error 0x%x).", Status); hLsa = NULL; goto End; } // // Create MSV1_0_S4U_LOGON structure // dwMessageLength = sizeof(MSV1_0_S4U_LOGON) + (2 + wcslen(szDomain) + wcslen(szUsername)) * sizeof(WCHAR); pS4uLogon = (PMSV1_0_S4U_LOGON)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, dwMessageLength); if (pS4uLogon == NULL) { fprintf(stderr, "HeapAlloc failed (error %u).", GetLastError()); goto End; } pS4uLogon->MessageType = MsV1_0S4ULogon; pbPosition = (PBYTE)pS4uLogon + sizeof(MSV1_0_S4U_LOGON); pbPosition = InitUnicodeString(&pS4uLogon->UserPrincipalName, szUsername, pbPosition); pbPosition = InitUnicodeString(&pS4uLogon->DomainName, szDomain, pbPosition); // // Misc // strcpy_s(TokenSource.SourceName, TOKEN_SOURCE_LENGTH, "S4UWin"); InitLsaString(&OriginName, "S4U for Windows"); AllocateLocallyUniqueId(&TokenSource.SourceIdentifier); // // Add extra SID to token. // // If the application needs to connect to a Windows Desktop, Logon SID must be added to the Token. // pGroups = (PTOKEN_GROUPS)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, sizeof(TOKEN_GROUPS) + 2*sizeof(SID_AND_ATTRIBUTES)); if (pGroups == NULL) { fprintf(stderr, "HeapAlloc failed (error %u).", GetLastError()); goto End; } pGroups->GroupCount = 1; pGroups->Groups[0].Attributes = SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY; pGroups->Groups[0].Sid = pLogonSid; // // If an extra SID is specified to command line, add it to the pGroups structure. // if (argc==3) { bResult = ConvertStringSidToSid(argv[2], &pExtraSid); if (bResult == TRUE) { pGroups->GroupCount = 2; pGroups->Groups[1].Attributes = SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY; pGroups->Groups[1].Sid = pExtraSid; } else { fprintf(stderr, "Unable to convert SID (error %u).", GetLastError()); } } // // Call LSA // Status = LsaLogonUser( hLsa, &OriginName, Network, // Or Batch ulAuthenticationPackage, pS4uLogon, dwMessageLength, pGroups, // LocalGroups &TokenSource, // SourceContext &pvProfile, &dwProfile, &logonId, &hTokenS4U, "aLimits, &SubStatus ); if (Status!=STATUS_SUCCESS) { printf("LsaLogonUser failed (error 0x%x).\n", Status); goto End; } printf("LsaLogonUser: OK, LogonId: 0x%x-0x%x\n", logonId.HighPart, logonId.LowPart); // // Create process with S4U token. // si.cb = sizeof(STARTUPINFO); si.lpDesktop = TEXT("winsta0\\default"); // // Warning: szCommandLine parameter of CreateProcessAsUser() must be writable // szCommandLine = (LPTSTR)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, MAX_PATH * sizeof(TCHAR)); if (szCommandLine == NULL) { fprintf(stderr, "HeapAlloc failed (error %u).", GetLastError()); goto End; } if (ExpandEnvironmentStrings(szSrcCommandLine, szCommandLine, MAX_PATH) == 0) { fprintf(stderr, "ExpandEnvironmentStrings failed (error %u).", GetLastError()); goto End; } // // CreateProcessAsUser required SeAssignPrimaryTokenPrivilege but no need to be activated. // bResult = CreateProcessAsUser( hTokenS4U, NULL, szCommandLine, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE, NULL, TEXT("c:\\"), &si, &pi ); if (bResult == FALSE) { printf("CreateProcessAsUser failed (error %u).\n", GetLastError()); goto End; } End: // // Free resources // if (Msv1_0Name.Buffer) HeapFree(g_hHeap, 0, Msv1_0Name.Buffer); if (OriginName.Buffer) HeapFree(g_hHeap, 0, OriginName.Buffer); if (pLogonSid) HeapFree(g_hHeap, 0, pLogonSid); if (pExtraSid) LocalFree(pExtraSid); if (pS4uLogon) HeapFree(g_hHeap, 0, pS4uLogon); if (pGroups) HeapFree(g_hHeap, 0, pGroups); if (pvProfile) LsaFreeReturnBuffer(pvProfile); if (hLsa) LsaClose(hLsa); if (hToken) CloseHandle(hToken); if (hTokenS4U) CloseHandle(hTokenS4U); if (pi.hProcess) CloseHandle(pi.hProcess); if (pi.hThread) CloseHandle(pi.hThread); return EXIT_SUCCESS; }
int LsaLogon(HANDLE *hToken, char homeDir[MAX_PATH], char *user, char *pkBlob, int pkBlobSize, char *sign, int signSize, char *data, int dataSize, int dataFellow) { int exitCode = 1; NTSTATUS ntStat = 0; LSA_STRING logonProcName; LSA_STRING originName; LSA_STRING authPckgName; HANDLE hLsa = NULL; LSA_OPERATIONAL_MODE securityMode; /* * Impersonation, "weak" token returned from network logon. * We can't create process as other user via this token. */ HANDLE hWeakToken = NULL; /* * Login data. */ LsaAuth *lsaAuth = NULL; ULONG lsaAuthSize = 0; ULONG authPckgId = 0; TOKEN_SOURCE srcToken; PVOID profile = NULL; ULONG profileSize; LUID logonId; QUOTA_LIMITS quotas; NTSTATUS loginStat; debug("-> LsaLogon()..."); /* * We check only hToken arg, becouse other args are tested in AllocLsaAuth(). */ debug("Checking args..."); FAIL(hToken == NULL); /* * Setup lsa strings. */ debug("Setting up LSA Strings..."); FAIL(InitLsaString(&logonProcName, "sshd-logon")); FAIL(InitLsaString(&originName, "NTLM")); FAIL(InitLsaString(&authPckgName, "SSH-LSA")); /* * Enable needed privilege to current running process. */ EnablePrivilege("SeTcbPrivilege", 1); /* * Register new logon process. */ debug("LsaRegisterLogonProcess()..."); NTFAIL(LsaRegisterLogonProcess(&logonProcName, &hLsa, &securityMode)); /* * Retrieve Authenticated Package ID. */ debug("Retrieving Authentification Package ID..."); NTFAIL(LsaLookupAuthenticationPackage(hLsa, &authPckgName, &authPckgId)); /* * Allocate LsaAuth struct. */ debug("Allocating LsaAuth struct..."); FAIL(AllocLsaAuth(&lsaAuth, user, pkBlob, pkBlobSize, sign, signSize, data, dataSize, dataFellow)); lsaAuthSize = lsaAuth -> totalSize_; /* * Create TOKEN_SOURCE part */ debug("Setting up TOKEN_SOURCE..."); FAIL(AllocateLocallyUniqueId(&srcToken.SourceIdentifier) == FALSE); memcpy(srcToken.SourceName, "**sshd**", 8); /* * Try to login using LsaAuth struct. */ debug("Login attemp..."); NTFAIL(LsaLogonUser(hLsa, &originName, Network, authPckgId, lsaAuth, lsaAuthSize, NULL, &srcToken, &profile, &profileSize, &logonId, &hWeakToken, "as, &loginStat)); debug("login status: %x...", loginStat); //FAIL(WideCharToMultiByte( CP_UTF8, 0, profile, -1, homeDir, MAX_PATH, NULL, NULL)==0); //memcpy(homeDir, profile, MAX_PATH*sizeof(wchar_t)); lstrcpyW(homeDir, profile); debug("homedir = [%ls]", (char *) homeDir); //strcpy(homeDir, profile); //PrintToken(hToken); /* * Duplicate 'weak' impersonation token into Primary Key token. * We can create process using duplicated token. */ debug("Duplicating token..."); FAIL(DuplicateTokenEx(hWeakToken, MAXIMUM_ALLOWED, NULL, SecurityImpersonation, TokenPrimary, hToken) == 0); exitCode = 0; fail: if (exitCode) { switch(ntStat) { case STATUS_LOGON_FAILURE: { debug("SSH-LSA authorization failed. " "(err = %u, ntStat = %x).", GetLastError(), ntStat); exitCode = 0; break; } case STATUS_NO_SUCH_PACKAGE: { debug("SSH-LSA package not found. " "(err = %u, ntStat = %x).", GetLastError(), ntStat); break; } default: { debug("Cannot logon using LSA package (err = %u, ntStat = %x).", GetLastError(), ntStat); } } hToken = NULL; } else { debug("LsaLogon : OK."); } /* * Clean up. */ CloseHandle(hWeakToken); LsaFreeReturnBuffer(profile); EnablePrivilege("SeTcbPrivilege", 0); LsaDeregisterLogonProcess(hLsa); ClearLsaString(&logonProcName); ClearLsaString(&originName); ClearLsaString(&authPckgName); debug("<- LsaLogon()..."); return exitCode; }