Thursday, September 8, 2011

Chasing duplicate SPN's

If you have problems with duplicate service principal names causing authentication problems in your domain, you can use a variety of tools to work on this. But first lets look at why duplicate SPN's are an issue.

To understand this problem, here is a basic explanation of the Kerberos authentication flow:

1) User accesses a resource application
2) Resource application tells user to authenticate
3) User connects to domain controller looking for a Kerberos service ticket for that service
4) Domain controller searches for an account with that service principal name
    a) If there is one in the same domain, use that one
    b) If there is more than one in the same domain, results may vary
    c) If there is more than one in multiple domains, results may vary
5) User receives ticket from Domain Controller
6) User presents ticket to resource application
7) Resource application account (computer or service account) attempts to decrypt the ticket to verify it.
    a) If the ticket was encrypted to them, authentication works
    b) If the ticket was encrypted to one of the other duplicate SPN accounts, decryption will fail, and access is denied.


Detection:

All your domain controllers will be logging events when duplicate SPN's are encountered. Unfortunately between 2008 and pre-2008 OS's, the event log source data is different. So when searching event logs you will have to account for this in some way. My example below will pull all the duplicate spn events and just strip out only the conflicted SPN record.

2000/2003 Domain controller:

Get-WMIObject -Computer MyDomainController -Filter "Logfile='system' and eventcode = 11 and sourcename='KDC' and type=5" -Class Win32_NtLogEvent | Foreach-Object { $_.insertionstrings[0]

2008 Domain controller:

Get-WMIObject -Computer MyDomainController -Filter "Logfile='system' and eventcode = 11 and sourcename='Microsoft-Windows-Kerberos-Key-Distribution-Center' and type=5" -Class Win32_NtLogEvent | Foreach-Object { $_.insertionstrings[0] }


Note: This type of query is a bit slow, but better than some methods. If you integrate a timewritten >= ########## this may greatly improve the speed of the event log query against the remote machine.

Take this information and you can use something like queryspn.vbs to look at any specific SPN to see what accounts are configured to use it. After that, analyze which account really needs it, then "setspn -D" the invalid entries away.

Wednesday, September 7, 2011

Generating complex passwords in powershell

Occasionally I'm required to create a password for a user and have never bothered to get around to using a proper random generation system. I decided to check around for powershell examples of this. There is one good example on google that uses [System.Web.Security.Membership]::GeneratePassword(), but apparently I need to upgrade .NET framework everywhere to get that one to work. There are a few longer and so-so examples out there. I decided to whip together something of my own to avoid the use of certain characters, while giving some flexibility as to content and length.




#Generate password

#Requires -Version 2
Param(
 [parameter(mandatory=$false)][int]$length=8,
 [parameter(mandatory=$false)][switch][alias("u")]$upper,
 [parameter(mandatory=$false)][switch][alias("n")]$numeric,
 [parameter(mandatory=$false)][switch][alias("s")]$symbol,
 [parameter(mandatory=$false)][switch][alias("m")]$maxcomplexity
)

function get-digit {
 return (Get-Random -Minimum 0 -Maximum 10)
}

function get-letter([bool]$is_upper) {
 if ($is_upper) {
  $letter = [char](Get-Random -Minimum 65 -Maximum 91)
 } else {
  $letter = [char](get-random -minimum 97 -maximum 123)
 }
 return $letter 
}

function get-validsymbol([int]$set) {
 #get a symbol from the ASCII table, but skip using *, \, `, ", and '
 switch($set) {
  1 { $symbol = Get-Random -Minimum 33 -Maximum 48}
  2 { $symbol = Get-Random -Minimum 58 -Maximum 65}
  3 { $symbol = Get-Random -Minimum 91 -Maximum 97}
  4 { $symbol = Get-Random -Minimum 123 -Maximum 127}  
 }
 while($symbol -match "34|42|44|92|96") {
  $symbol = get-validsymbol $set
 }
 return $symbol
}

function get-symbol {
 $setnum = Get-Random -Minimum 1 -Maximum 5
 return ([char](get-validsymbol $setnum))
}

#main

#look at input parameters and generate available charset limits
$values = @()
if ($maxcomplexity) {
 $values = (1,2,3,4)
} else {
 $values += 1
 if ($upper) {
  $values += 2
 }
 if ($numeric) {
  $values += 3
 }
 if ($symbol) {
  $values += 4
 }
}
$values
Write-Host
$password = ""
for ($i = 0; $i -lt $length; $i++) {
 $set = Get-Random -Minimum 1 -Maximum ($values.length+1)
 $values[$set-1]
 switch ($values[$set-1]) {
  1 { $char = get-letter $false }
  2 { $char = get-letter $true }
  3 { $char = get-digit }
  4 { $char = get-symbol }
 }
 $password += $char
}
return $password


<#
.SYNOPSIS

generate-password.  Create a random password. 

.DESCRIPTION

Generate a random password which by default contains only lowercase letters.  Additionally
you can specify length, use of uppercase letters, numeric characters, and use of symbols.

.PARAMETER length

Length of the password

.PARAMETER upper

User upper case and lower case letters (alias u)

.PARAMETER numeric

Use numbers (alias n)

.PARAMETER symbol

Use symboles (alias s)

.PARAMETER Maxcomplexity

Use all forms of password complexity (alias m)

.EXAMPLE

generate-password

Create an 8 char password of lower case letters.

.EXAMPLE

generate-password -length 6 -m

Create a 6 char password with letters (upper/lower), numbers and symbols

#>