Sacrificial session


How many times you have run into the problem of replacing the current user Kerberos TGT? Creating the sacrificial process is the way to prevent that from happening. If the current user TGT will be replaced, the authentication to the domain resources will be done using the new TGT. To get back to the original Kerberos TGT, the user credentials are required. The example below shows the situation when user1 TGT got replaced by user2 Kerberos ticket. The user1 can no longer authenticate to the User1_share on the fileserver, instead, the User2_share is available.


The Rubeus is used to request user2 TGT.

Rubeus.exe asktgt /user:user2 /aes256:2E6C6E16D610A9AA1FDDC69CD0E8C47163F90DA18AA5A0F6FFEF872EB28BCD16 /ptt

In the LUID (locally unique identifier) 0x4be79, can only be user2 TGT found. Pasted%20image%2020230219093108

The authentication now is only possible to the User2_share instead of User1_share. Pasted%20image%2020230219093341

Requesting "back" user1 TGT using user1 credentials and checking the authentication to the shares. The shared folder on fileserver User2_share is no longer available. Pasted%20image%2020230219093820

Tools that can spawn the sacrificial process are usually using the Win32 API CreateProcessWithLogonW. The CreateProcessWithLogonW can create a new process with the given credentials and run a selected executable in the new security context. The credential check is not being done by the CreateProcessWithLogonW thus dummy credentials can be used. One of the important flags in the CreateProcessWithLogonW is dwLogonFlags, and one of the options the flag can take is LOGON_NETCREDENTIALS_ONLY (value 0x00000002).

Microsoft description of LOGON_NETCREDENTIALS_ONLY

Use the specified credentials on the network only. The new process uses the same token as the caller, but the system creates a new logon session within LSA, and the process uses the specified credentials as the default credentials. This value can be used to create a process that uses a different set of credentials locally than it does remotely. This is useful in inter-domain scenarios where there is no trust relationship.

The system does not validate the specified credentials. Therefore, the process can start, but it may not have access to network resources.

With the LOGON_NETCREDENTIALS_ONLY flag, we can specify any credentials we want, the process will not validate the credentials, and it creates a separate LUID (locally unique identifier) in Logon Session. in the LSASS. The user can have multiple LUIDs (locally unique identifiers) with different credentials in Logon Session as represented in the below graph. Pasted%20image%2020230221131709

We can quickly check if the Logon Session can have more than one LUID by spawning a few sacrificial sessions via Rubeus and Mimikatz. To check spawned process associated Authentication ID (LUID used for network authentication), the LsaGetLogonSessionData function can be used. All spawned sacrificial sessions will have user2 credentials. Pasted%20image%2020230221143726


I've written a small custom program that will take ProcessID as an argument and create a handle to that process using OpenProcess. Next it will get a handle to the access token associated with that process using OpenProcessToken and retrieve the AuthenticationID (LUID) with GetTokenInformation. This will give us the LUID used for the network authentication but to use it with LsaGetLogonSessionData we have to create a pointer to the memory where the LUID is located.

//creating pointer to the LUID from TokenStats.AuthenticationId
IntPtr ptrToLuid = Marshal.AllocHGlobal(Marshal.SizeOf(TokenStats.AuthenticationId));
Marshal.StructureToPtr(TokenStats.AuthenticationId, ptrToLuid, true);

With all set, the LsaGetLogonSessionData with return the pointer to the address of a SECURITY_LOGON_SESSION_DATA structure containing information on the logon session specified by LUID. The spawned sacrificial processes have 1408 (HeidiSQL), 1080 (cmd.exe spawned by Mimikatz) and 4960 (cmd.exe spawned by Rubeus) process id. Pasted%20image%2020230221144154

None of the LUIDs are pointing toward the user2 whose credentials were used to create the sacrificial session instead they are pointing toward the george local administrator account from which context the Mimikatz was used and user1 from which context Rubeus was used. The credentials used by the authentication LUID are stored in the LSASS process, and only by querying LSASS, it's possible to check which username is associated with the authentication LUID. With the help of Sharpkatz tool, it's possible to query LSASS and find out which user is under authentication LUID.

SharpKatz.exe --command kerberos




As we can see, by querying the LSASS we were able to discover the username user2 behind the LUIDs which will be used for network authentication, specifically we are interested here in the Kerberos authentication package. This proves that Logon Session might have more than one LUID associated with it.

Let's find out how the tools are creating the sacrificial process. The focus will be on the Rubeus and Mimikatz and also a quick example of a custom tool.

Rubeus has an option createnetonly which can create a sacrificial process utilizing CreateProcessWithLogonW. It can create a sacrificial process of, for example, cmd.exe or powershell.exe.

Rubeus description of createnetonly

The createnetonly action will use the CreateProcessWithLogonW() API to create a new hidden (unless /show is specified) process with a SECURITY_LOGON_TYPE of 9 (NewCredentials), the equivalent of runas /netonly. The process ID and LUID (logon session ID) are returned. This process can then be used to apply specific Kerberos tickets to with the ptt /luid:0xA.. parameter, assuming elevation. This prevents the erasure of existing TGTs for the current logon session.

To spawn the sacrificial process with Rubeus just execute the below command, which will spawn cmd.exe with dummy credentials. Notice that the spawned cmd.exe has a different LUID 0x1580a9.

Rubeus.exe createnetonly /program:"C:\Windows\System32\cmd.exe" /show


In the created process, the Kerberos TGT can be requested and the ticket will be in a different LUID (0x1580a9) and thus will not overwrite the current user Kerberos tickets. Let's request user2 TGT in the newly spawned sacrificial session and check if the tickets are overwritten in the original session.

Rubeus.exe asktgt /user:user2 /password:user2 /domain:cloud.local /dc:cloud-dc /ptt

The requested Kerberos ticket landed only in the new LUID 0x1580a9 and it's possible to authenticate to the User2_share as user2 in the sacrificial session and to User1_share as user1 in the original session. Pasted%20image%2020230219102034


It’s also possible to inject the Kerberos ticket into spawned sacrificial process, but this requires elevated privileges as we are injecting code into another process. First, create a new sacrificial process. The created process has LUID 0x19e34a.

Rubeus.exe createnetonly /program:"C:\Windows\System32\cmd.exe" /show


From the original session, inject the requested TGT to the LUID 0x19e34a.

Rubeus.exe asktgt /user:user2 /password:user2 /domain:cloud.local /dc:cloud-dc /ptt /luid:0x19e34a

The injection can't be done as the user1 hasn't got high privileges / SeDebug privilege. Pasted%20image%2020230219104648


It's also possible to specify username, password and domain to spawn sacrificial process.

Rubeus.exe createnetonly /program:"C:\Windows\System32\cmd.exe" /show /username:testuser /password:testuser /domain:testdomain

Mimikatz creates the sacrificial process with its PTH module. Mimikatz unlike the Rubeus injects the password hash into the LSASS process which requires high privileges. Mimikatz is executed from the context of the local administrator. By default, the cmd.exe is spawned and Mimikatz replaces the NT HASH in LSASS, the Kerberos tickets are not present yet in the LUID 0x2a8194. Pasted%20image%2020230220125338

When the user will try to authenticate to network resources, the authentication process will use the injected hash to request the TGT and get TGS to access the resources. Pasted%20image%2020230220125901

The Mimikatz hash injection into LSASS is very helpful if, for example, we would like to use found domain credentials with the HeidiSQL application to connect to the MSSQL server. Pasted%20image%2020230222131332

The spawned HeidiSQL process will use the authentication LUID associated with user2. To take advantage of injected hash, we can't forget to tick the use of Windows authentication. Now it will be possible to authenticate to the MSSQL with injected hash. Pasted%20image%2020230222131734

With the help of Microsoft documentation, it’s also straightforward to build your custom tool which will spawn the chosen process. In the below code sample, the hardcoded dummy credentials are used with CreateProcessWithLogonW.

string username = "test_user";
string domain = "whatever.local";
string password = "supersecretpassword$$";
string processPath = args[0];
string command = null;
CreateProcessWithLogonW(username, domain, password, LOGON_NETCREDENTIALS_ONLY, processPath, command, 0x00000010, (UInt32)0, Environment.CurrentDirectory, ref si, out pi);

The code is also using the GetTokenInformation Win32 API to retrieve the LUID of the current process and spawned sacrificial process.

var hToken = IntPtr.Zero;
OpenProcessToken(pi.hProcess, 0x000A, out hToken);
int TokenInfLength;
bool Result;
Result = GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenStatistics, out TokenStats, Marshal.SizeOf(TokenStats), out TokenInfLength);

Console.WriteLine("[+] Spawned LUID               : 0x{0:X8}", TokenStats.AuthenticationId);


The sacrificial session can be very helpful during the engagement as the user authentication will not be affected by our Kerberos ticket manipulations. The main applications which can help with spawning sacrificial sessions are Rubeus and Mimikatz, which will create a new process with new authentication LUID in the current Logon Session. With the well-documented CreateProcessWithLogonW Win32 API function the custom tool can be written which has one big advantage, it’s not known to the AV signature base detection.

Previous Post