Wednesday, May 30, 2012

Secure DoD Drive Wiping with SCCM

I mentioned in my last post a need to do a secure drive wipe on remote systems. There are several good and free options for wiping drives, but not many that can be executed remotely. Microsoft however offers a nice tool called SDelete as part of their free Sysinternals Suite. SDelete will do a DoD 5220.22-M sanitization of free space on a drive, but by itself isn't very good at destroying all of the data on a disk. That is where WinPE comes in. Using WinPE and sdelete is something that Derek Meier has blogged about previously. We're going to take this a step further and use SCCM to make this process remotely executable. Plus, we'll use a package to deliver the files to WinPE, so there is no need to modify the boot.wim or do any custom WinPE builds.

We'll basically use an SCCM Task Sequences to boot into Windows PE and format the drive with a single, empty C: partition. Then we'll use sdelete to handle the DoD wipe of the empty partition.

To do this, we'll need to get SDelete to Windows PE. So to start, we'll create a package with SDelete.exe (which can be downloaded from here: http://technet.microsoft.com/en-us/sysinternals/bb897443.aspx).

We'll also create a file called sdelete.reg with the following contents:

----------------- begin sdelete.reg -----------------

Windows Registry Editor Version 5.00


[HKEY_CURRENT_USER\Software\Sysinternals\SDelete]
"EulaAccepted"=dword:00000001

----------------- end sdelete.reg -----------------

Once the package with these two files is created, we can now move to our task sequence.

Note: These steps only apply for systems with a single hard drive. If you have multiple hard drives, you'll need to to repeat steps 3 and 4 for each disk in the system.

Step 1: Add a task to Restart into WinPE using your favorite boot.wim.
Step 2: We're going to copy over the files from our sdelete package. To do this, add a General -> Run Command Line task. Check the box by Package and select the package created with the sdelete files in it. The command line the I use whenever I'm copying files from a package to WinPE is:
xcopy.exe ".\*.*" "%WinDir%" /E /C /Q /H /R /Y /I

(Ignore the whole Enable Remote Control section in this image. If you want to learn about using VNC to connect to WinPE, see my previous post)

Step 3: Now that the files are in place, we'll add another Run Command Line task, which we'll use to accept the Systernals EULA for sdelete.exe. This time our command line will be:
regedit /s sdelete.reg 
Set the Start in field to %WinDir%, since that is where we copied the files.
Step 4: Now we'll create on big C partition to fill the entire drive. Add a Disks -> Format and Partition Disk task.  Set Disk number to and Disk type to Standard(MBR). Create a new Volume. Set the Partition type to Primary and set it to use 100% of the free space. The File system should be NTFS and I recommend checking the Quick Format box (the seven pass 0/1 fill takes long enough).


Step 5: Now that we have one big, empty C: partition, its time to use sdelete to make sure that the old data is unrecoverable. For this we'll go back to a Run Command Line task. Our command will be:
sdelete.exe -p 7 -c -z
The -p 7 gives us seven passes. The -c tells it to clean free space. The -z tells it to zero free space.
Its important to set the Start in to C:\, otherwise you might try and clean the X: drive.

Now you're ready to go destroy some data! Just advertise your task sequence and watch it go to work making information unrecoverable. I think it goes without saying, but you should be very careful about where this is advertised, especially if you're using mandatory assignments.




Monday, May 28, 2012

Remote Control During SCCM OSD (Without Modifying the Boot.wim)

I recently had a discussion with my good friend and SCCM guru, Michael Orr, about remote controlling WinPE during the SCCM OSD process. He added Tight VNC to his boot.wim so that he could remote in and see what is happening if problems started popping up during OSD.

I recently took on a project to do a remote DoD wipe of computer harddrives, and decided to try and tackle it with SCCM OSD and Microsoft's sdelete (more on that later). Since the computers are going to be in multiple sites, all of which are remote to me, I decided that remote control could be very beneficial. Instead of trying to modify the boot.wim, I decided to add it on the fly with a package. Here's what I did.

First, we'll need the latest UltraVNC binaries (I'm using 1.0.9.6), which can be accessed here: http://www.uvnc.com/downloads/ultravnc/100-download-ultravnc-10962.html. Make sure that you grab the Binaries, and not the MSI or other installers. The binary zip file should include three files:
  • winvnc.exe
  • vncviewer.exe
  • SCHook.dll
If you're not familiar with UltraVNC, the vncviewer.exe is what we'll use later to initiate the remote control session, so you can just set it aside for now.

Go ahead and run winvnc.exe, which will open the configuration window for the UltraVNC server. Make sure that Display Query Window is unchecked and that you have set a password.
Hit OK, and you should now have a winvnc.ini file with these settings in it. 

Now open the ini file and find the line for path, and change it to path=x:\windows.

Save your new ini file and then create a new package within SCCM that contains it, winvnc.exe and schook.dll. I called my package UltraVNC 1.0.9.6 (WinPE). There isn't a need to create any programs because we're going to grab the files from a task sequence (and don't forget to push it out to your distribution points).

Now we're ready to tweak our OSD task sequence to allow VNC remote control.

Edit the task sequence, and right after the step where you Restart into Windows PE, we're going to add some commands. I put mine in a group called UltraVNC, but to each your own.

All of these entries are going to be Run Command Line entries (Add -> General -> Run Command Line).

First, we're going to disable the Windows firewall by using the Command Line: 
wpeutil DisableFirewall

Next we're going to copy the UltraVNC files into WinPE. To do this, our Command Line will be:
xcopy.exe ".\*.*" "%WinDir%" /E /C /Q /H /R /Y /I
Check the box by Package, and select the package where you put the UltraVNC binaries.
This will take all of the files from that package and copy them into the X:\Windows\System32 directory.

Now we'll kick off the UltraVNC server by using the following command:
cmd.exe /c start %windir%\winvnc.exe
Its important that we use the cmd.exe /c start, because if we just called winvnc.exe directly, the task sequence would halt while it waited for winvnc.exe to close.

Now your UltraVNC server is running, so you can launch your vncviewer.exe and put in the IP address of your remote client!

As a final note, remote control won't really do you much good if you don't know the IP address where you want to connect. Typically with OSD, when a computer reboots into WinPE it retains the same IP address that it had previously, so a simple nslookup can typically give you that if you don't already know it. Another good suggestion is to do what my buddy Mike does and to use BGInfo within WinPE to stamp the IP address on the screen. That will allow remote users who call in to easily provide you an IP address.

Monday, May 21, 2012

Kerberos Token Size Script

As Active Directory infrastructures age and groups accumulate on user accounts, a users Kerberos token size increases. When the Kerberos token grows beyond the maximum allowed size, authentication problems abound and there are all manner of unfortunate side-effects.

There are several great KB articles that go into to detail about the Kerberos token size problem and potential resolutions. Two of the better ones are:
  http://support.microsoft.com/kb/327825
  http://blogs.technet.com/b/surama/archive/2009/04/06/kerberos-authentication-problem-with-active-directory.aspx
So I'm not going to rehash the details of these articles, but instead focus on identifying whether or not you have a Kerberos token size problem.

To answer that question, we'll turn to PowerShell. From KB327825, we know that we can estimate the token size using the following formula:
    TokenSize = 2*(1200 + 40d + 8s)

d = # of domain local groups + # of universal groups outside the user's domain + # of groups in SID history.
s = # of security global groups + # of universal groups in a user's domain
1200 = Estimated value for ticket overhead.
2* = When users authenticate to a domain controller, Microsoft recommends that you double the estimate.

In most cases I've dealt with, group accumulation was the primary culprit, so to simplify the script we'll leave out the SID History and external domains.

This script takes a user account as an argument, and then looks at its group membership to create an estimated token size:


# Estimates Kerberos Tickets Size based upon the formula here:
# http://support.microsoft.com/kb/327825
#   TokenSize = 1200 + 40d + 8s
#   This formula uses the following values:
#   d: The number of domain local groups a user is a member of plus the number of universal groups outside the user's account domain plus the number of groups represented in security ID (SID) history.
#   s: The number of security global groups that a user is a member of plus the number of universal groups in a user's account domain.
#   1200: The estimated value for ticket overhead. This value can vary depending on factors such as DNS domain name length, client name, and other factors.
#   In scenarios in which delegation is used (for example, when users authenticate to a domain controller), Microsoft recommends that you double the token size.
# Created By: Jeremy Jackson (aisforaction@gmail.com)


if (!$Args[0]) {
write-host "Usage: .\UserTokenEstimate.ps1 <username>"
break
}


$username=$Args[0]
$groupfile = $username + "_groups.txt"
$tokenfile = $username + "_tokeninfo.txt"


function global:Count-Groups {


param($CN)


$LDAP="LDAP://"+$CN

write-host $CN
$CN  | out-file $groupfile -append

$user = [adsi]$LDAP
$groupSearcher = New-Object DirectoryServices.DirectorySearcher($user)
$groupSearcher.PageSize = 1000
$results = $groupSearcher.FindOne()


if ($results.properties.item("groupType") -eq "-2147483646") {$Type="Global"}
if ($results.properties.item("groupType") -eq "-2147483644") {$Type="DomainLocal"}
if ($results.properties.item("groupType") -eq "-2147483640") {$Type="Universal"}


if ($Groups -notcontains $CN) {
$script:Groups += $CN
if ($Type -eq "Global") {$script:GlobalGroups += $CN}
if ($Type -eq "DomainLocal") {$script:DomainLocalGroups += $CN}
if ($Type -eq "Universal") {$script:UniversalGroups += $CN}
}


foreach ($Group in $results.properties.item("memberOf")) {
if ($Groups -notcontains $Group) {
Count-Groups($Group)
}
}
}


function global:Estimate-Token {


param($Name,[string]$ADSPath)

$ADSPath = $ADSPath.Replace("LDAP://","")

Set-Variable -Name Groups -Value @() -Scope Script
Set-Variable -Name GlobalGroups -Value @() -Scope Script
Set-Variable -Name DomainLocalGroups -Value @() -Scope Script
Set-Variable -Name UniversalGroups -Value @() -Scope Script


Count-Groups $ADSPath


$TokenSize =  1200 + ($DomainLocalGroups.Count * 40) + ($UniversalGroups.Count * 40) + ($GlobalGroups.Count * 8)
$DCTokenSize = $TokenSize * 2


[string]$TokenInfo = $Name + "," + $TokenSize + "," + $DCTokenSize + "," + $Groups.Count + "," + $DomainLocalGroups.Count  + "," + $UniversalGroups.Count + "," +  $GlobalGroups.Count + "," + $ADSPath

write-host $TokenSize : $ADSPath

$TokenInfo.Replace(" ","") | out-file $tokenfile -append


}


$Header = "Name,Token Size,Double Token Size,Groups,Domain Local Grops,Universal Groups,Global Groups"
$Header | out-file $tokenfile -append


$strFilter = "(&(objectCategory=person)(objectClass=user)(name=$username))"


$objDomain = New-Object System.DirectoryServices.DirectoryEntry


$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.PageSize = 1000
$objSearcher.Filter = $strFilter
$objSearcher.SearchScope = "Subtree"


$objSearcher.PropertiesToLoad.Add("name")


$colResults = $objSearcher.FindAll()


foreach ($objResult in $colResults) {
$objItem = $objResult.Properties
Estimate-Token $objItem.name $objItem.adspath
}

Sunday, May 20, 2012

Group Policy Preferences NetBIOS Targeting Bug

I recently ran across a bug in Group Policy Preferences around the use of Item-Level Targeting with the Computer NetBIOS name. I scoured the Internet and found no other references to the bug, so I decided that I would post something here.

To replicate the bug, some very specific steps have to be taken, which is one of the reasons that there isn't much reference to it online. It occurs when you use the Computer NetBIOS name target, and you have a multi-character wildcard (*), followed by some text, and then a single-character wildcard (?). Even more specifically, the issue only seems to occur if the text between the wildcards is less than half of the length of the applying computer's name. If that is the case, the computer fails to apply the preference.

I know that description is a little tough to follow, so let's look at this way...
The filter has to have two wildcards, first a multi-character (*) and then a single-character (?). We'll call the number of characters between the two N. So if your filter is *abc123?, N would be equal to 6. We'll call the length of the computer name C. So if your computer name is myabc1234, C would be equal to 9. Anytime that C/2 > N, the filter fails and the preference does not apply.

For example:
Your filter is *serv? (N=4)
Your computer is financeserv1 (C=12)
Using C/2 > N, we get 6 > 4, which is true so the filter would fail and the preference would not apply.

Microsoft acknowledges that this is a bug, but since it is pretty targeted and there are several workarounds, we are not likely to see a patch for it anytime soon. Most likely they will try to resolve the issue in Windows 8 RTM (a bug report has been filed), and then backrev it to Windows 7.

So what are the workarounds? Of course the most straightforward is to change the target string so that this bug doesn't apply by changing wildcard types (eg. two multi-character wildcards) or so that N is a long enough string that C/2 > N would not ever be true. Assuming that you can't change the filter, the next simplest fix is to switch the Computer Name target from the default of NetBIOS to DNS, where the issue does not seem to occur. You could also use a WMI query target, where the wildcards would work properly (though WMI queries take longer to execute and may slow Group Policy processing some).