Group Policy Preferences Passwords

Hello again.  Today I want to talk about Group Policy Preferences Passwords.

So as most of you probably know, you can manage local accounts across many computers using Active Directory Group Policy Preferences. You can create them, delete them, and change their passwords.

GPP Adding a User*About to add a local user to all computers to whom this GPO is linked.*

This feature can also be really useful for adding already existing AD security groups to, say, the local administrators group on all the machines.  For instance, I could use this console to add the "DOMAIN\Accounting Managers" group to the local Administrators group on all the computers in the Accounting OU. That sort of thing.  However, one slightly more interesting piece of knowledge is that local users created here, will have their passwords stored in SYSVOL, in a format that is decipherable.

I've been seeing this "news" pop up around the internet here and there the past couple of weeks, so I wanted to speak to it.  Here is one such article on how to "crack" the passwords stored via GPP. Here's another one.  Now I want to make clear that these people are not "hacking" or "exploiting" Windows 2008 Group Policy Preferences by pointing this out.  This is well-known functionality that is fully documented by Microsoft.  Microsoft has warned about this, more than once, and even posts the AES decryption key themselves on MSDN!

I suppose that MS could pop up a warning dialog when editing that particular GPP item, plainly telling the administrator that this should not be used for sensitive, administrative accounts.  I have no doubt that there are companies of all sizes that are using this today without realizing that it's not secure.  Even though it is fully documented, you can't count on people reading the documentation.  RTFM is as true now as it was in 1979.

It's important to remember that this code was inherited by Microsoft when they acquired another software company, not originally written by Microsoft.  MS bought what used to be known as PolicyMaker, and integrated their stuff into what we know now as Group Policy Preferences.  Since there were already customers using PolicyMaker, that bit with the not-so-secure passwords needed to stay in so as to preserve compatibility with existing customers. So MS is aware, they made a deliberate choice to leave that functionality in knowing that it was insecure, and it will hopefully get improved down the road.

As they say, "compatibility is deliberately repeating someone else's mistakes."

From the "No Not All My Stuff Is Patched Yet" Dept.

This is a public service announcement.

If you have a Windows 2008 R2 server without SP1 or without this hotfix, and you also have a monitoring application or script that uses WMI to query for service information from the Win32_Service WMI class on a regular basis, you might just get burned really badly by a memory leak that will have the WMI service gobbling up >500MB of memory until it crashes.  You might then have to roll back hours of work and hope the Microsoft bug didn't cause any cluster failovers or dropped Exchange mailbox stores across a couple dozen servers.

2012 Scripting Games Post-Game

So I finished up my participation in the 2012 Scripting Games Advanced category a few days ago. They haven't finished all the grading yet, but all the events have been completed. (10 total scripts in 10 business days.) Here are few of my takeaways:

  • It was 100% Powershell, so it really should have been called the Powershell Games, but I realize Ed's blog used to have a lot of VB Script on it too before PS really came into the spotlight, so I guess the name is sort of legacy. His blog is not known as "Hey, Powershell Guy!" after all. Besides, I don't know of anyone else holding a similar event, so I guess he gets to use whatever name he wants.
  • I don't think there's any chance of me winning first place in the Advanced category, but I should (hopefully) finish in the top 10. Which, I guess isn't all that bad considering how many participants there were from all over the world. Leaderboards should be viewable here, but like I said the grading is not finished yet and so the leaderboards are still going to be changing.
  • The Games were reasonably challenging, and I did learn a few new tricks and best practices along the way. For instance, creating my own custom objects, and adding those to a collection of objects, has become much more natural for me. I will probably post all of the scripts I wrote and some commentary about them in a later post - I want to make sure the deadlines for the Games are completely passed before I do that.
  • Even though several days were given to complete each event, I turned in my submission for each event on the same day it was released. I have a pretty single-track mind when it comes to things like finishing code. It's often all I can think about or concentrate on until I finish, especially if there's any sort of deadline involved. Not only that, but I have other things like a job which also demand my time and energy -- unlike those damn Germans with their 6 days off for Easter holiday and 2 months a year of vacation. (Just teasing, Germany.)
  • I felt like a couple of the scenarios were not very well-defined. One could start scripting for the scenario given, but then several hours later go back and see several confused reader's posts, asking for Ed to clarify a certain piece of the scenario, and then after reading Ed's responses, do something differently in your own script. Even worse, I saw some inconsistency in the way different judges judged people's scripts. For instance, Ed posted the official rules and grading criteria before the games began. One of those grading criteria was "avoid using aliases." I think that's perfectly reasonable, as aliases are good for quick, interactive commands, but when writing a long, complex script, aliases often make it even harder for someone else to follow. (Aliases are things like "?" instead of "Where-Object" or "gci" instead of "Get-ChildItem.") But, browsing the judge's comments of other people's scripts, I would see a judge commenting on the participant's "excellent use of aliases!" So in that regard I don't feel like all the judges were on the same page, which is unfortunate, because it seems like only 1, and maybe sometimes 2, of the ~35 total judges ever grade any one script, so depending on exactly which judge you get will significantly impact your score.
  • I don't like a judge giving me a score on my script, but not leaving any comments at all. (Especially if it's a crappy score like 3/5.) That said, I understand that the judges are all just volunteers that have their own lives, and there are hundreds of participants, so the judges are overworked and probably in a hurry.

So all in all, even if my comments above sound negative, I'm really meaning them to be constructive. I did enjoy the 2012 Scripting Games and I'm really happy that Ed put forth the time and effort (which I know must have been substantial) to organize them!

C# applications with "install" or "setup" in the name and Application Compatibility

Hello ladies and gents.

I've been busy the past few days creating a very complex (for me) .NET application in C#. I want to talk about an issue that I banged my head on for a few hours, because the solution is out there and easy to find on the internet, but every person who posted the answer on a forum or MSDN article was making an assumption that I knew something already that I didn't know in order to arrive at the solution, and so that missing piece of knowledge was keeping me from joining the ranks of those who knew how to solve this problem. What can I say, I'm a novice programmer.

I'm using Visual Studio 2010, and I have created a C# project. I'm using Windows 7 64bit. This project that I have created has the word "install" in the name. Apparently, any time that Windows Vista or 7 see the user executing a program that has the word "setup" or "install" in it, it triggers something deep within the bowels of Windows to cough up this incisive message:

appcompat*Thank you that's very helpful now go away*

I don't like the principle behind this behavior. I have read that this box will only pop up if you have the magic word in your executable name and that executable does not add an entry to the Add/Remove Programs list by the time it exits, which could indicate a failed installation. Or it could indicate nothing went wrong at all, which from my experience is the case 100% of the time. In fact I'd be scared to hit that "Reinstall using recommended settings" button, because I definitely have no idea what that might do. Furthermore I've read that some install applications do put a new entry in the Add/Remove Programs list and yet it still generates this popup.

I've been using Windows Vista and Windows 7 for a long time, and I have never once found this to be helpful in any way. But now this behavior has really gone and annoyed me by popping up every time I test during my development.

So I started researching the problem and I was immediately led to believe that this could be easily solved by editing the manifest file in my application's project in Visual Studio. Here is what you add to your manifest file to make it not show that Program Compatibility popup:

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
   <application>
      <!--The ID below indicates application support for Windows Vista -->
      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
      <!--The ID below indicates application support for Windows 7 -->
      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
   </application>
</compatibility>

OK... I can't find my manifest file. I've looked through all the menus and tabs and buttons here in Visual Studio... hrm... maybe I just need to edit this installer.manifest file that's sitting in the same output directory as my application and then rebuild it! Nope, that didn't work. It just seems to wipe out my changes and revert the file to default every time I build the solution... my project settings were set to "Embed manifest with default settings."  So even though the default manifest was there in the output directory, any modifications you make to it just get wiped out.

Finally, I realized that I just need to generate a new manifest file for myself. In the Solution Explorer pane, right-click your project and choose "Add --> New Item..." and add an "Application Manifest File." It should pop up under the Resources folder in your project, whether you already had one or not. Now you can edit that manifest to your heart's content, and the changes will not get wiped out. You'll notice that the section of XML I pasted above sort of already exists in your new manifest file, so just use your head and paste in only the couple of relevant lines in the right places. ;)  You will also notice that if you go back to your Project's properties, you should see it pointing at the manifest file you just created, instead of saying "Embed manifest with default settings."

No more "Program Compatibility Assistant" popup!  I feel like an idiot sometimes...

Now  that you know about creating and editing your own application manifest file, you are well on your way to doing other things with it, such as having the application request its own administrator access from UAC, instead of, say, simply checking to see whether the current user is an administrator or not, and then having the application say "Sorry, you're not an administrator so I won't run.  Please try executing the program again by right-clicking on my icon and choosing 'Run as administrator...'" Which unfortunately I have seen too many times. Follow up with this article.

A List of NICs, IPs, MACs, Physical Locations, etc.

I'm back, finally.

I was recently challenged with trying to not only enumerate all the network adapters on a system across dozens of different operating system versions and hardware platforms, but also to try to figure out where they are physically in the machine, remotely, without being able to see the actual hardware.

The short answer is you can't.

The long answer is you can't... do it scriptomatically without the assistance of vendor-specific software, such as the HP network configuration software and maybe an API or WBEM queries... but that's only going to cover one specific hardware platform. I need to consistently gather this data across not only Proliants, but Poweredges, VMs, desktop workstations, anything that runs Windows. Windows doesn't know where in space your network adapters are. By that I mean Windows doesn't know which physical port on your 4-port NIC is the third one from the left, etc. This would be why there is seemingly no rhyme or reason as to which network adapter Windows assigns "Local Area Network", "Local Area Network #2", "Local Area Network #3", etc. The installed NICs are enumerated randomly, as evidenced by the fact that you may get different results for which NIC port is assigned to which network connection every time you re-install Windows on a multi-NIC machine. I have heard that some particularly anal administrators even go so far as to install Windows, then delete all the Network Connections that are out of order, and continue removing and letting Windows reinstall them until they are all in the "correct" order. There is also a theory that NIC manufacturers of multi-port NICs should give each port on the card sequential MAC addresses, starting from the port closest to the PCI bus. So you might be able to infer something from that, but that's not something I would put money on for thousands of NICs with dozens of manufacturers.

Furthermore, "NIC teaming" throws yet another wrench into this, as now you can no longer rely on what Windows thinks the MAC address of a teamed adapter is, or what the cabinet switch thinks the MAC address is on a given switch port that has a teamed NIC plugged in to it.

I can get you all the information that Windows does have though, including (apparent) MAC addresses, IPs, and "Location Information" as read from the registry. This is that "Bus 0, Device 8, Function 25" stuff that you might have seen in Device Manager. It might be useful in drawing some correlations, but it's still not going to tell you much about physically where all these NICs are.

So without further ado, here are the scripts. The first one is Powershell. The second one is the exact same but ported to VB Script, for compatibility with older versions of Windows. Note the operating system version check in the VB Script.

Powershell:

$ErrorActionPreference = 'Stop'
$nics = Get-WmiObject Win32_NetworkAdapter
$cfgs = Get-WmiObject Win32_NetworkAdapterConfiguration

Write-Host "`nPhysical NICs In No Particular Order"
Write-Host "------------------------------------`n"
foreach ($_ in $nics)
{
	Try
	{
		if($_.PNPDeviceID.StartsWith('PCI'))
		{
			$registryKey = Get-Item HKLM:\System\CurrentControlSet\Enum\$($_.PNPDeviceID)
			$keyValues   = Get-ItemProperty $registryKey.PSPath
			$regSplit    = $keyValues.LocationInformation.Split(";") 
			$location    = $regSplit[2].Replace('(','').Replace(')','')
			$locSplit    = $location.Split(",")			
			
			Write-Host "Name    : $($_.Name)"
			Write-Host "MAC     : $($_.MACAddress)"
			Write-Host "Location: Bus $($locSplit[0]), Device $($locSplit[1])`, Function $($locSplit[2])"
			$mac = $_.MACAddress
			foreach ($cfg in $cfgs)
			{
				if($cfg.MACAddress -eq $mac -And $cfg.IPAddress)
				{
					Write-Host "IP      : $($cfg.IPAddress)"
				}
			}
			Write-Host " "	
		}	
	}
	Catch {	}
}

VB Script:

Option Explicit
const HKEY_LOCAL_MACHINE = &H80000002

Dim nic, objNICs, objCfgs, objWMIService, objReg, objOSVer
Dim strWMIQuery, strRegistryKey, strValue, strLocInfo, strBus, strDevice, strFunction, strOSMajor
Dim arrSplitKey, arrSplitLoc, arrOSBuild
Dim mac, cfg, ip, v

strWMIQuery = "SELECT * FROM Win32_NetworkAdapter"
Set objWMIService = GetObject("winmgmts:\\.\root\CIMv2")
Set objNICs = objWMIService.ExecQuery(strWMIQuery)
strWMIQuery = "SELECT MACAddress,IPAddress FROM Win32_NetworkAdapterConfiguration"
Set objCfgs = objWMIService.ExecQuery(strWMIQuery)
strWMIQuery = "SELECT Version FROM Win32_OperatingSystem"
Set objOSVer = objWMIService.ExecQuery(strWMIQuery)

For Each v in objOSVer
	arrOSBuild = Split(v.Version,".")
Next

strOSMajor = arrOSBuild(0)

Wscript.Echo "Physical NICs In No Particular Order"
Wscript.Echo "------------------------------------"

For Each nic In objNICs
	If StrComp(Left(nic.PNPDeviceID,3),"PCI",1) = 0 Then
		Set objReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv")
		strRegistryKey = "System\CurrentControlSet\Enum\" & nic.PNPDeviceID				
		objReg.GetStringValue HKEY_LOCAL_MACHINE,strRegistryKey,"LocationInformation",strValue
		If CInt(strOSMajor) >= 6 Then
			arrSplitKey = Split(strValue,";")
			strLocInfo = arrSplitKey(2)
			strLocInfo = Replace(strLocInfo,"(","")
			strLocInfo = Replace(strLocInfo,")","")
			arrSplitLoc = Split(strLocInfo,",")
		End If
		
		Wscript.Echo "Name    : " & nic.Name
		Wscript.Echo "MAC     : " & nic.MACAddress
		
		If CInt(strOSMajor) >= 6 Then
			Wscript.Echo "Location: Bus " & arrSplitLoc(0) & ", Device " & arrSplitLoc(1) & ", Function " & arrSplitLoc(2)
		Else
			Wscript.Echo "Location: " & strValue
		End If
		
		mac = nic.MACAddress
		For Each cfg In objCfgs
			If StrComp(cfg.MACAddress,mac) = 0 And isNull(cfg.IPAddress) = False Then
				For Each ip In cfg.IPAddress
					Wscript.Echo "IP      : " & ip
				Next				
			End If
		Next
		Wscript.Echo " "
		If isObject(objReg) Then Set objReg = Nothing
	End If
Next

The output looks like this:

The IPs are not shown on the second adapter because it's switched off right now and thus doesn't have any IPs. My first idea for improvement of the Powershell version (I don't invest much time into improving VBS,) is making custom objects out of the output instead of just doing Write-Hosts. The power of Powershell is in its ability to deal with objects, and so you should try to keep everything as objects for as long as possible. Once you've spit it out on the screen in a Write-Host statement for example, you can no longer pass it along the pipeline, etc.

Thanks to Kelvin Wong and Server Fault for helping me research this.