Using PowerShell to retrieve CAC Information

, ,

Using PowerShell to retrieve CAC Information

In this article, we will cover Using PowerShell to retrieve CAC Information

So today, we have a very niche PowerShell tutorial.  If you’re part of DOD or Army IT, you’ll probably recognize this as the scripting component of the FASCN project. For everyone else, I hope there’s something here you can use.

Scenario

Users have received new smart cards that contain the fourth certificate to be used for Domain authentication. These certificates have a value, that while based on the EDIPI contained in existing certificates, is expanded and will only be used for this one purpose.   Several forests full of domains full of users now have UserPrincipalName (UPN) values that must be updated to take advantage of the new certificate.

Solution

PowerShell. Thanks for coming out, everyone. Have a nice night…

Actually, this is a very straightforward script if all you care about is “lines of code.”  Right now, we only wish to collect the FASCN value from each user upon login, so we need something that can run as a scheduled task in the user’s context. We also want to make sure that we don’t get the wrong values. For example, if two smart cards are inserted into a computer (e.g. maybe a technician has logged into a user’s PC to install software), we don’t want the certificate information from the technician’s card to update the account information of the user who was originally logged in.

The Code

  • To start the script, we want to isolate the current user’s UPN, which should be their EDIPI number if you’re rocking that DoD life.  I don’t want to require the ActiveDirectory module on each user’s computer, so we’re going to get it locally with whoami /upn instead. A little bit of string manipulation later, and the @mil is gone, leaving just the number.
  • Next, we have to isolate the certificates from the CAC.  The certificate containing the FASCN is referred to as the Authentication certificate, so we look for that in the FriendlyName value. However, we want to make sure we get a certificate that matches the right user, so we’re also going to make sure the EDIPI value exists in the Subject value. Lastly, we don’t want any expired or cached certificates getting in our way, and checking for the HasPrivateKey value guarantees that the certificate belongs to a CAC that is currently inserted in the reader.
  • We also want to get the user’s email address for this project, so we’re going to check the same values again, except against the Signature certificate. Variables corresponding to both the Signature and Authentication certificates are saved so we can do more work.
  • Both the FASCN and the email address are values saved in their certificates’ Subject Alternative Name values. Unfortunately, the raw data for these values is hideous, so we need a couple of COMobjects to decode them. You can’t use the same one to decode both values, so I create two.  While I’m at it, I save the raw data to some Strings so it’s easier to get.
  • Once you’ve used the COMObjects to decode the raw data. We want to get the strValue values from each of them. There may be some null entries hanging around. So just throw a quick FOR loop in there to only get the entries you want.
  • Now that you have the FASCN and Email values from the certificate. You can output them to a CSV file, write them to a text file. Then write back to Active Directory, or maybe send it to a printer. So you can wallpaper your bodega with useless information.

Now that you have an outline, here is the code that does all the work:

$myEDIPI = (whoami /upn).replace('@mil','')
$comOBJS = @()
$PIVCERT = gci Cert:\CurrentUser\My | ? {$_.Subject -like "*$($myEDIPI)*" -and $_.FriendlyName -like "Authentication -*" -and $_.HasPrivateKey -eq $true}     	 
$EMAILCERT = gci Cert:\CurrentUser\My | ? {$_.Subject -like "*$($myEDIPI)*" -and $_.FriendlyName -like "Signature -*" -and $_.HasPrivateKey -eq $true}     	 
$Extensions=$pivcert.Extensions | Where-Object {$_.Oid.FriendlyName -eq "Subject Alternative Name"}       	 
$EmailAddress=$EMAILCERT.Extensions | Where-Object {$_.Oid.FriendlyName -eq "Subject Alternative Name"}
$comOBJS += new-object -ComObject X509Enrollment.CX509ExtensionAlternativeNames       	 
$comOBJS += new-object -ComObject X509Enrollment.CX509ExtensionAlternativeNames       	 
$FASCNString=[System.Convert]::ToBase64String($Extensions.RawData)       	 
$emailString=[System.Convert]::ToBase64String($EmailAddress.RawData)
$comObjs[0].InitializeDecode(1, $FASCNString)       	 
$FASCN = ($comOBJS[0].AlternativeNames | ?{$_.StrValue -like "*$($myEDIPI)*"}  ).strvalue
$comOBJS[1].InitializeDecode(1, $emailString)
$email = ($comOBJS[1].AlternativeNames | ?{$_.StrValue -like "*@mail.mil"}).strvalue
write-host "FASCN discovered: " $FASCN
write-host "Email discovered: " $email

Read more Using PowerShell to retrieve CAC Information

Set-CMDistributionPoint Maintenance Mode

, , , , , , , , ,

SCCM Reboot DECODED:: How to make a PC Cancel, Start, Extend or Change mandatory reboot to non-mandatory on the fly.

, , , , , , , , ,

SCCM Reboot DECODED:: How to make a PC Cancel, Start, Extend or Change mandatory reboot to non-mandatory on the fly.

SCCM Reboot DECODED:: How to make a PC Cancel, Start, Extend or Change mandatory reboot to non-mandatory on the fly.

In the past to stop a PC from rebooting when you didn’t want it to people would stop the ccmexec service and do a shutdown /a

But here is the problem with that..

  1.  Shutdown /a will normally tell you that no shutdown is pending.
  2.  Any number of things can restart ccmexec.

First, how to check if a reboot is pending VS a reboot is GOING to happen

On a win10 PC or has powershell 5 installed, use this

#Detect pending reboot:
Invoke-CimMethod -Namespace root/ccm/ClientSDK -ClassName CCM_ClientUtilities -MethodName DetermineIfRebootPending

On a win7 or old powershell 2 you need to use these.

#
([wmiclass]'ROOTccmClientSDK:CCM_ClientUtilities').DetermineIfRebootPending().RebootPending
([wmiclass]'ROOTccmClientSDK:CCM_ClientUtilities').DetermineIfRebootPending().IsHardRebootPending
([wmiclass]'ROOTccmClientSDK:CCM_ClientUtilities').DetermineIfRebootPending().RebootDeadline
([wmiclass]'ROOTccmClientSDK:CCM_ClientUtilities').DetermineIfRebootPending().NotifyUI

It will spit out something like this..

#PC with no pending reboot.

DisableHideTime     : 12/31/1969 2:00:00 PM
InGracePeriod       : False
IsHardRebootPending : False
NotifyUI            : False
RebootDeadline      : 12/31/1969 2:00:00 PM
RebootPending       : False
ReturnValue         : 0
PSComputerName      :

#PC with pending NON-mandatory reboot.

DisableHideTime : 12/31/1969 12:00:00 PM
InGracePeriod : False
IsHardRebootPending : False
NotifyUI : True
RebootDeadline : 12/31/1969 12:00:00 PM
RebootPending : True
ReturnValue : 0
PSComputerName :

#PC with pending mandatory reboot, notice the time stamp.

DisableHideTime : 5/1/2019 3:28:56 PM
InGracePeriod : True
IsHardRebootPending : False
NotifyUI : True
RebootDeadline : 5/1/2019 11:28:56 PM
RebootPending : True
ReturnValue : 0
PSComputerName :

 

Now for the magic…..

Everything that SCCM uses for knowing when and if it should reboot comes from here.

HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData

On a PC with no reboot pending, this key is empty.

So starting with that, this is how we can CANCEL a pending reboot.

#CANCEL a pending reboot
Remove-Item -path 'HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData';
Remove-Item -path 'HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Updates Management\Handler\UpdatesRebootStatus\*';
Remove-ItemProperty -name * -path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired';

#on PS2.0, "Remove-ItemProperty" doesn't work, so use this.
#Remove-Item -path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired';

shutdown -a 
Restart-Service ccmexec -force

But what if you just want to cancel the mandatory reboot and change it to a non-mandatory reboot so the user will still get the popup telling them they “need to” reboot?

#change mandatory reboot to  non-mandatory reboot
Set-Itemproperty -path 'HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData' -name 'RebootBy' -value 0;
Restart-Service ccmexec -force

What if we just want to extend the time of a mandatory reboot?

Example: In your client settings you have your reboot countdown set to 10 hours…. A user calls and says it’s going to reboot in 10 min and needs it extended… This will reset that users countdown timer back to 10 hours.

#Reset SCCM reboot countdown timer.
$time = [DateTimeOffset]::Now.ToUnixTimeSeconds()
Set-Itemproperty -path 'HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData' -name 'RebootBy' -value $time;
Restart-Service ccmexec -force

What if you want to kick off the built in SCCM reboot WITH the client settings countdown timer?
NOTE: Setting $Time to 0 will popup the non-mandatory reboot window.

$time = [DateTimeOffset]::Now.ToUnixTimeSeconds()
New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData' -Name 'RebootBy' -Value $time -PropertyType QWord -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData' -Name 'RebootValueInUTC' -Value 1 -PropertyType DWord -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData' -Name 'NotifyUI' -Value 1 -PropertyType DWord -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData' -Name 'HardReboot' -Value 0 -PropertyType DWord -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData' -Name 'OverrideRebootWindowTime' -Value 0 -PropertyType QWord -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData' -Name 'OverrideRebootWindow' -Value 0 -PropertyType DWord -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData' -Name 'PreferredRebootWindowTypes' -Value @("4") -PropertyType MultiString -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData' -Name 'GraceSeconds' -Value 0 -PropertyType DWord -Force -ea SilentlyContinue;

 

NOTE:: In all my tests after you run The powerShell command it takes about 30 seconds for the client to respond since after you restart the service it has to do all it’s internal checks to figure out what’s new.

#WeaponizedAutismFTW

Read more SCCM Reboot DECODED