Skip to content

Commit

Permalink
Add RunAsS4U tool
Browse files Browse the repository at this point in the history
  • Loading branch information
diversenok committed Dec 15, 2021
1 parent 0bf7151 commit abeb52d
Show file tree
Hide file tree
Showing 4 changed files with 1,183 additions and 2 deletions.
2 changes: 1 addition & 1 deletion NtUtils
Submodule NtUtils updated 1 files
+9 −9 NtUtils.Lsa.Sid.pas
6 changes: 5 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# NtTools

A collection of random system tools for Windows.
This project is a collection of random system tools for Windows that are interesting enough to publish but too small or simple to put into a dedicated repository.

Tool | Description
------------ | -----------
**RunAsS4U** | RunAsS4U allows administrators to start processes under other users without knowing their passwords. It relies on the Service for User (S4U) logon in its operation.
221 changes: 221 additions & 0 deletions RunAsS4U/RunAsS4U.dpr
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
program RunAsS4U;

{$APPTYPE CONSOLE}
{$R *.res}

uses
Ntapi.WinNt,
Ntapi.ntstatus,
Ntapi.ntseapi,
Ntapi.WinUser,
Ntapi.ProcessThreadsApi,
NtUtils,
NtUtils.Tokens,
NtUtils.Tokens.Info,
NtUtils.Tokens.Impersonate,
NtUtils.Tokens.Logon,
NtUtils.Threads,
NtUtils.Profiles,
NtUtils.Security.Sid,
NtUtils.Security.Acl,
NtUtils.Processes.Snapshots,
NtUtils.Processes.Create,
NtUtils.Processes.Create.Win32,
NtUtils.Environment.User,
NtUtils.Lsa.Sid,
NtUtils.WinUser,
NtUtils.Console,
NtUiLib.Errors;

function Main: TNtxStatus;
var
hxSystemProcess: IHandle;
hxToken: IHandle;
hxProfileKey: IHandle;
AccountName: TTranslatedName;
Sid: ISid;
MakeAdmin: Boolean;
DefaultDacl: IAcl;
AdditionalGroups: TArray<TGroup>;
ProcessOptions: TCreateProcessOptions;
ProcessInfo: TProcessInfo;
begin
writeln('RunAsS4U: run programs as other users without knowing passwords. ' +
'(c) diversenok'#$D#$A);

// Using S4U requires the TCB privilege; try enabling it
Result := NtxAdjustPrivilege(NtxCurrentProcessToken, SE_TCB_PRIVILEGE,
SE_PRIVILEGE_ENABLED, True);

if not Result.IsSuccess then
Exit;

if Result.Status = STATUS_NOT_ALL_ASSIGNED then
begin
// TCB is not available; we need to impersonate a SYSTEM token
Result := NtxAdjustPrivilege(NtxCurrentProcessToken,
SE_IMPERSONATE_PRIVILEGE, SE_PRIVILEGE_ENABLED);

if not Result.IsSuccess then
Exit;

// Winlogon is a good candidate for getting a SYSTEM token
Result := NtxOpenProcessByName(hxSystemProcess, 'winlogon.exe',
PROCESS_QUERY_LIMITED_INFORMATION, [pnCurrentSessionOnly]);

if not Result.IsSuccess then
Exit;

// Open the primary token for duplication
Result := NtxOpenProcessToken(hxToken, hxSystemProcess.Handle,
TOKEN_DUPLICATE);

if not Result.IsSuccess then
Exit;

// Convert the token into an impersonation one
Result := NtxDuplicateTokenLocal(hxToken, TokenImpersonation,
SecurityImpersonation);

if not Result.IsSuccess then
Exit;

// Impersonate it
Result := NtxSafeSetThreadToken(NtxCurrentThread, hxToken,
[siSkipLevelCheck]);

if not Result.IsSuccess then
Exit;
end;

write('Account name: ');

// Lookup the username and split it into domain + user
Result := LsaxCanonicalizeName(ReadString(False), AccountName);

if not Result.IsSuccess then
Exit;

// Make sure to include the current window stations's logon SID into the token
Result := UsrxQuerySid(GetProcessWindowStation, Sid);

if not Result.IsSuccess then
Exit;

if Assigned(Sid) then
AdditionalGroups := [TGroup.From(Sid,
SE_GROUP_ENABLED_BY_DEFAULT or SE_GROUP_ENABLED or SE_GROUP_LOGON_ID
)]
else
// As a fallback, use NT AUTHORITY\RESTRICTED instead
AdditionalGroups := [TGroup.From(
RtlxMakeSid(SECURITY_NT_AUTHORITY, [SECURITY_RESTRICTED_CODE_RID]),
SE_GROUP_ENABLED_BY_DEFAULT or SE_GROUP_ENABLED
)];

write('Should the new process have administrative privileges? [y/n]: ');
MakeAdmin := ReadBoolean;
writeln;

if MakeAdmin then
AdditionalGroups := AdditionalGroups + [
// Add NT AUTHORITY\Local account and member of Administrators
TGroup.From(RtlxMakeSid(SECURITY_NT_AUTHORITY, [
SECURITY_LOCAL_ACCOUNT_AND_ADMIN_RID
]), SE_GROUP_ENABLED_BY_DEFAULT or SE_GROUP_ENABLED),

// Add BUILTIN\Administrators
TGroup.From(RtlxMakeSid(SECURITY_NT_AUTHORITY, [
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS
]), SE_GROUP_ENABLED_BY_DEFAULT or SE_GROUP_ENABLED or SE_GROUP_OWNER)
];

// Always include NT AUTHORITY\INTERACTIVE
AdditionalGroups := AdditionalGroups + [TGroup.From(RtlxMakeSid(
SECURITY_NT_AUTHORITY, [SECURITY_INTERACTIVE_RID]),
SE_GROUP_ENABLED_BY_DEFAULT or SE_GROUP_ENABLED)];

// Logon the user via S4U
Result := LsaxLogonS4U(hxToken, AccountName.DomainName, AccountName.UserName,
TTokenSource.New('RunAsS4U'), AdditionalGroups);

if not Result.IsSuccess then
Exit;

if not MakeAdmin then
begin
// Filter the token to strip admin groups and privileges
Result := NtxFilterTokenInline(hxToken, LUA_TOKEN);

if not Result.IsSuccess then
Exit;

// Lower its integrity
Result := NtxSetIntegrityToken(hxToken, SECURITY_MANDATORY_MEDIUM_RID);

if not Result.IsSuccess then
Exit;

// We also need to correct the default DACL after filtration;
// construct it based on the new owner and logon SID from the token
Result := NtxMakeDefaultDaclToken(hxToken, DefaultDacl);

if not Result.IsSuccess then
Exit;

// Adjust the default DACL
Result := NtxSetDefaultDaclToken(hxToken, DefaultDacl);

if not Result.IsSuccess then
Exit;
end;

// Load the profile for the user
Result := UnvxLoadProfile(hxProfileKey, hxToken);

if not Result.IsSuccess then
Exit;

ProcessOptions := Default(TCreateProcessOptions);
ProcessOptions.Application := USER_SHARED_DATA.NtSystemRoot +
'\system32\cmd.exe';
ProcessOptions.Flags := [poNewConsole];
ProcessOptions.hxToken := hxToken;

// Prepare the correct environment for the user
Result := UnvxCreateUserEnvironment(ProcessOptions.Environment, hxToken,
False, False);

if not Result.IsSuccess then
Exit;

// Spawn CMD using the token
Result := AdvxCreateProcess(ProcessOptions, ProcessInfo);
end;

procedure RunMain;
var
Result: TNtxStatus;
Completed: Boolean;
begin
Completed := False;

try
Result := Main;

if Result.IsSuccess then
writeln('Success.')
else
writeln(Result.ToString);

Completed := True;
finally
if not Completed then
writeln('An exception occured :(');
end;
end;

begin
RunMain;
end.

Loading

0 comments on commit abeb52d

Please sign in to comment.