原本想利用组策略首选项,定义修改本地管理员账户密码。但是由于cpassword 漏洞,微软新补丁修复后已经禁用了该功能,根据微软知识库说明,采用提供的Invoke-PasswordRoll.ps1 脚本进行修改。

具体方法如下:

运行环境:域环境,执行用户必须具有远程主机的管理员权限。其实采用域管理员就可以。

执行脚本的主机 安装powershell 3.0


步骤:

  1. 在脚本同目录下,建立一个computerlist.txt 文档,里面写入要就该的主机FQDN。

  2. 运行powershell

  3. 根据脚本说明。首先加载脚本: ..\Invoke-PasswordRoll.ps1

  4. 然后执行命令:Invoke-PasswordRoll -ComputerName (Get-Content computerlist.txt) -LocalAccounts @("administrator") -TsvFileName "LocalAdminCredentials.tsv" -NoEncryption -PasswordLength 40

    注:以上命令读取computerlist.txt, 密码为随机生成,长度40位,新密码保存在LocalAdminCredentials.tsv。该文档未加密

    脚本说明里,另外一条命令:

    Invoke-PasswordRoll -ComputerName (Get-Content computerlist.txt) -LocalAccounts @("administrator","CustomLocalAdmin") -TsvFileName "LocalAdminCredentials.tsv" -EncryptionKey "Password1"

    为修改密码后,生成加密文档 LocalAdminCredentials.tsv 


    其他功能可打开脚本里面的注释写得相当清楚,本脚本可以帮助企业根据安全管理要求定期更换服务器的本地管理员密码,且每台服务器的本地管理员密码均为随机生成。

    因无法上传脚本受限,贴代码大家可复制保存为ps1 格式

注:windows2008r2 需要启用winrm 远程管理服务,在运行里,执行winrm quickcconfig。这样才能远程执行脚本。

windows2003 系统因为没有安装winrm服务,所以必须先手动安装winrm程序包。链接如下:

http://www.microsoft.com/zh-cn/download/details.aspx?id=4045


  1. function Invoke-PasswordRoll
    {
    <#
    .SYNOPSIS
    
    此脚本可用于将远程计算机上的本地帐户密码设置为随机密码。用户名/密码/服务器组合将保存在 CSV 文件中。可以使用管理员选择的密码对存储在 CSV 文件中的帐户密码进行加密,以确保不会将明文帐户密码写入磁盘中。可以使用此文件中的另一个函数对加密密码进行解密:ConvertTo-CleartextPassword
    
    
    Function: Invoke-PasswordRoll
    Author: Microsoft
    Version: 1.0
    
    .DESCRIPTION
    
    此脚本可用于将远程计算机上的本地帐户密码设置为随机密码。用户名/密码/服务器组合将保存在 CSV 文件中。可以使用管理员选择的密码对存储在 CSV 文件中的帐户密码进行加密,以确保不会将明文帐户密码写入磁盘中。可以使用此文件中的另一个函数对加密密码进行解密: ConvertTo-CleartextPassword
    
    .PARAMETER ComputerName
    
    要使用 PowerShell 远程处理对其运行脚本的一组计算机。
    
    .PARAMETER LocalAccounts
    
    应更改其密码的一组本地帐户。
    
    .PARAMETER TsvFileName
    
    将用户名/密码/服务器组合输出到其中的文件。
    
    .PARAMETER EncryptionKey
    
    对 TSV 文件进行加密所使用的密码。使用 AES 加密。只有存储在 TSV 文件中的密码才会加密,用户名和服务器名称将采用明文形式。
    
    .PARAMETER PasswordLength
    
    为本地帐户随机生成的密码的长度。
    
    .PARAMETER NoEncryption
    
    不要对存储在 TSV 文件中的帐户密码进行加密。这将导致明文密码被写入到磁盘中。
    	
    .EXAMPLE
    
    . .\Invoke-PasswordRoll.ps1    #加载此脚本文件中的函数
    Invoke-PasswordRoll -ComputerName (Get-Content computerlist.txt) -LocalAccounts @("administrator","CustomLocalAdmin") -TsvFileName "LocalAdminCredentials.tsv" -EncryptionKey "Password1"
    
    连接到存储在文件“computerlist.txt”中的所有计算机。如果系统中存在本地帐户“administrator”和/或“CustomLocalAdmin”,则其密码将更改为长度为 20(默认值)的随机生成密码。用户名/密码/服务器组合存储在 LocalAdminCredentials.tsv 中,且使用密码“Password1”对帐户密码进行 AES 加密。
    
    .EXAMPLE
    
    . .\Invoke-PasswordRoll.ps1    #加载此脚本文件中的函数
    Invoke-PasswordRoll -ComputerName (Get-Content computerlist.txt) -LocalAccounts @("administrator") -TsvFileName "LocalAdminCredentials.tsv" -NoEncryption -PasswordLength 40
    
    连接到存储在文件“computerlist.txt”中的所有计算机。如果系统中存在本地帐户“administrator”,则其密码将更改为长度为 40 的随机生成密码。用户名/密码/服务器组合存储在 LocalAdminCredentials.tsv 中,未进行加密。
    
    .NOTES
    要求:-必须安装 PowerShellv2 或更高版本 -必须在将对其运行脚本的所有系统上启用 PowerShell 远程处理
    
    脚本行为:-如果系统中存在本地帐户,但在 LocalAccounts 参数中未进行指定,则脚本会在屏幕上显示警告,以提醒您存在此本地帐户。出现这种情况时,脚本将继续运行。-如果在 LocalAccounts 参数中指定了本地帐户,但系统中不存在该帐户,则不会发生任何情况(不会创建帐户)。-此文件中包含的函数 ConvertTo-CleartextPassword 可用于对存储在 TSV 文件中已加密的密码进行解密。-如果无法连接到 ComputerName 中指定的服务器,PowerShell 将生成错误消息。-Microsoft 建议公司定期滚动所有本地和域帐户密码。
    
    #>
        [CmdletBinding(DefaultParameterSetName="Encryption")]
        Param(
            [Parameter(Mandatory=$true)]
            [String[]]
            $ComputerName,
    
            [Parameter(Mandatory=$true)]
            [String[]]
            $LocalAccounts,
    
            [Parameter(Mandatory=$true)]
            [String]
            $TsvFileName,
    
            [Parameter(ParameterSetName="Encryption", Mandatory=$true)]
            [String]
            $EncryptionKey,
    
            [Parameter()]
            [ValidateRange(20,120)]
            [Int]
            $PasswordLength = 20,
    
            [Parameter(ParameterSetName="NoEncryption", Mandatory=$true)]
            [Switch]
            $NoEncryption
        )
    
    
    #加载任何所需的 .net 类
        Add-Type -AssemblyName "System.Web" -ErrorAction Stop
    
    
    #这是脚本块,将在 ComputerName 中指定的每台计算机上执行
        $RemoteRollScript = {
            Param(
                [Parameter(Mandatory=$true, Position=1)]
                [String[]]
                $Passwords,
    
                [Parameter(Mandatory=$true, Position=2)]
                [String[]]
                $LocalAccounts,
    
    #在此处,我可以记录脚本所连接到的服务器名称,DNS 记录有时会变得很混乱,现在这样非常好。
                [Parameter(Mandatory=$true, Position=3)]
                [String]
                $TargettedServerName
            )
    
            $LocalUsers = Get-WmiObject Win32_UserAccount -Filter "LocalAccount=true" | Foreach {$_.Name}
    
    #检查计算机是否具有密码不会由此脚本滚动的任何本地用户帐户
            foreach ($User in $LocalUsers)
            {
                if ($LocalAccounts -inotcontains $User)
                {
                    Write-Warning "Server: '$($TargettedServerName)' has a local account '$($User)' whos password is NOT being changed by this script"
                }
            }
    
    #对于此服务器中存在的所有指定的本地帐户,更改密码
            $PasswordIndex = 0
            foreach ($LocalAdmin in $LocalAccounts)
            {
                $Password = $Passwords[$PasswordIndex]
    
                if ($LocalUsers -icontains $LocalAdmin)
                {
                    try
                    {
                        $objUser = [ADSI]"WinNT://localhost/$($LocalAdmin), user"
                        $objUser.psbase.Invoke("SetPassword", $Password)
    
                        $Properties = @{
                            TargettedServerName = $TargettedServerName
                            Username =  $LocalAdmin
                            Password = $Password
                            RealServerName = $env:computername
                        }
    
                        $ReturnData = New-Object PSObject -Property $Properties
                        Write-Output $ReturnData
                    }
                    catch
                    {
                        Write-Error "Error changing password for user:$($LocalAdmin) on server:$($TargettedServerName)"
                    }
                }
    
                $PasswordIndex++
            }
        }
    
    
    #在运行此脚本的客户端上生成密码,而非在远程计算机上。.NET 客户端配置文件中不提供 System.Web.Security。在运行该脚本的 # 客户端上进行此调用可确保只有 1 台计算机需要安装完整的 .NET 运行库(相对于已滚动密码的每个系统)。
        function Create-RandomPassword
        {
            Param(
                [Parameter(Mandatory=$true)]
                [ValidateRange(20,120)]
                [Int]
                $PasswordLength
            )
    
            $Password = [System.Web.Security.Membership]::GeneratePassword($PasswordLength, $PasswordLength / 4)
    
    #此操作绝对不会失败,但不管怎样,我还是在这里加入了健全性检查
            if ($Password.Length -ne $PasswordLength)
            {
                throw new Exception("Password returned by GeneratePassword is not the same length as required. Required length: $($PasswordLength). Generated length: $($Password.Length)")
            }
    
            return $Password
        }
    
    
    #主要功能 - 生成密码并通过远程方式进入计算机以更改指定的本地帐户的密码
        if ($PsCmdlet.ParameterSetName -ieq "Encryption")
        {
            try
            {
                $Sha256 = new-object System.Security.Cryptography.SHA256CryptoServiceProvider
                $SecureStringKey = $Sha256.ComputeHash([System.Text.UnicodeEncoding]::Unicode.GetBytes($EncryptionKey))
            }
            catch
            {
                Write-Error "Error creating TSV encryption key" -ErrorAction Stop
            }
        }
    
        foreach ($Computer in $ComputerName)
        {
    #需要为每个帐户生成 1 个可以更改的密码
            $Passwords = @()
            for ($i = 0; $i -lt $LocalAccounts.Length; $i++)
            {
                $Passwords += Create-RandomPassword -PasswordLength $PasswordLength
            }
    
            Write-Output "Connecting to server '$($Computer)' to roll specified local admin passwords"
            $Result = Invoke-Command -ScriptBlock $RemoteRollScript -ArgumentList @($Passwords, $LocalAccounts, $Computer) -ComputerName $Computer
    #如果正在使用加密,则在写入磁盘之前,使用用户提供的密钥对密码进行加密
            if ($Result -ne $null)
            {
                if ($PsCmdlet.ParameterSetName -ieq "NoEncryption")
                {
                    $Result | Select-Object Username,Password,TargettedServerName,RealServerName | Export-Csv -Append -Path $TsvFileName -NoTypeInformation
                }
                else
                {
    #筛选掉返回的 $null 条目
                    $Result = $Result | Select-Object Username,Password,TargettedServerName,RealServerName
    
                    foreach ($Record in $Result)
                    {
                        $PasswordSecureString = ConvertTo-SecureString -AsPlainText -Force -String ($Record.Password)
                        $Record | Add-Member -MemberType NoteProperty -Name EncryptedPassword -Value (ConvertFrom-SecureString -Key $SecureStringKey -SecureString $PasswordSecureString)
                        $Record.PSObject.Properties.Remove("Password")
                        $Record | Select-Object Username,EncryptedPassword,TargettedServerName,RealServerName | Export-Csv -Append -Path $TsvFileName -NoTypeInformation
                    }
                }
            }
        }
    }function ConvertTo-CleartextPassword
    {
    <#
    .SYNOPSIS
    此函数可用于对通过函数 Invoke-PasswordRoll 实现加密存储的密码进行解密。
    
    Function: ConvertTo-CleartextPassword
    Author: Microsoft
    Version: 1.0
    
    .DESCRIPTION
    此函数可用于对通过函数 Invoke-PasswordRoll 实现加密存储的密码进行解密。
    
    
    .PARAMETER EncryptedPassword
    
    存储在 TSV 文件中的已加密密码。
    
    .PARAMETER EncryptionKey
    
    用于执行加密的密码。
    
    
    .EXAMPLE. .\Invoke-PasswordRoll.ps1    #加载此脚本文件中的函数
    ConvertTo-CleartextPassword -EncryptionKey "Password1" -EncryptedPassword 76492d1116743f0423413b16050a5345MgB8AGcAZgBaAHUAaQBwADAAQgB2AGgAcABNADMASwBaAFoAQQBzADEAeABjAEEAPQA9AHwAZgBiAGYAMAA1ADYANgA2ADEANwBkADQAZgAwADMANABjAGUAZQAxAGIAMABiADkANgBiADkAMAA4ADcANwBhADMAYQA3AGYAOABkADcAMQA5ADQAMwBmAGYANQBhADEAYQBjADcANABkADIANgBhADUANwBlADgAMAAyADQANgA1ADIAOQA0AGMAZQA0ADEAMwAzADcANQAyADUANAAzADYAMAA1AGEANgAzADEAMQA5ADAAYwBmADQAZAA2AGQA"
    
    对存储在 TSV 文件中的已加密密码进行解密。
    
    #>
        Param(
            [Parameter(Mandatory=$true)]
            [String]
            $EncryptedPassword,
    
            [Parameter(Mandatory=$true)]
            [String]
            $EncryptionKey
        )
    
        $Sha256 = new-object System.Security.Cryptography.SHA256CryptoServiceProvider
        $SecureStringKey = $Sha256.ComputeHash([System.Text.UnicodeEncoding]::Unicode.GetBytes($EncryptionKey))
    
        [SecureString]$SecureStringPassword = ConvertTo-SecureString -String $EncryptedPassword -Key $SecureStringKey
        Write-Output ([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToCoTaskMemUnicode($SecureStringPassword)))}