发布于 

PowerShell 在普通用户下使用管理员权限运行

在普通用户桌面下使用特定用户权限来运行程序

开始使用一段时间 PowerShell 之后,应该会发现有不少东西是要管理员权限才能运行,但是公司内大部分人的权限不是管理员才是常态。

在 Windows 桌面使用中有个小技巧,就是按着 Shift 再去对程序右键,右键菜单会多出一个以其他用户身份运行,在弹出的对话框里面输入管理员账号,这个程序就会运行在管理员权限下了。

Shift + 右键 以其他用户身份运行

但正常情况下,对着 PowerShell 的.ps1 脚本这样 Shift+右键是没有这个选项给你的,虽然你可以先 Shift+右键来以管理员运行 PowerShell.exePowerShell_ISE.exe,再去打开.ps1 脚本文件来让.ps1 脚本以管理员权限运行,本来 Shift+右键就有点麻烦了,这样套娃来运行就更麻烦了。

CMD 脚本的.bat 文件倒是可以用 Shift + 右键来操作,所以有不少简单的脚本我还是会习惯直接拿.bat 来写。

PowerShell 中的 -Credential “凭据”

在 PowerShell 的学习使用中会时不时看到有些命令有这样一个参数 "-Credential" ——“凭据”,这个就是用来让命令以指定用户来运行的参数。
例如:

以指定用户凭据来运行命令

1
2
3
4
5
# 弹窗让你输入账号密码获取对应账号的权限
$AdminUserCredential = Get-Credential

# 这就是 PowerShell版本的 RUNAS /user:Administrator
Start-Process -FilePath regedit.exe -Credential $AdminUserCredential

这样你就能以你刚刚输入的账号权限来运行 regedit.exe 注册表编辑器了

但是不是所有命令都支持加 "-Credential" 参数的,我希望整个脚本都以管理员运行,有什么方法吗?

检查脚本是否为管理员权限运行,不是就重新以指定用户凭据运行脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 检测是否为管理员权限的函数
function Check-IsElevated {
$id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$p = New-Object System.Security.Principal.WindowsPrincipal($id)
if ($p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)) {
return $true
}
else {
return $false
}
}

# 不是管理员权限就 Get-Credential 获取账户凭据后再以账户权限运行,$PSCommandPath 是特殊变量,代表脚本文件的路径。
if ( -Not $(Check-IsElevated) ) {
Write-Host "当前账户非管理员权限"
$AdminUserCredential = Get-Credential
Start-Process -FilePath "powershell.exe" -ArgumentList "$PSCommandPath" -Credential $AdminUserCredential # -WindowStyle Hidden
exit
}

# ------!!!------ 下面的内容就全都是管理员运行的了 ------!!!------

# 启动 PowerShell ISE
Start-Process -FilePath "powershell_ise.exe"

# 在C:\Windows\ 下面创建一个 001AdminTest 文件夹
New-Item -Path "C:\Windows\001AdminTest" -ItemType Directory

要是我连账号密码都不想输入,想直接丢给普通用户用,有什么方法吗?

用户凭据账号密码直接写在脚本中

不要把管理员凭据明文写入到脚本中给普通用户使用,这是个极高风险的操作。

1
2
3
4
5
6
7
8
# 管理员凭据
$AdminUserName = "$env:COMPUTERNAME\Administrator" # 拥有管理员权限的账号,这里写的是本地管理员
$AdminUserPassword = "AdminPassword" # 拥有管理员权限的账号的密码
$SecureAdminUserPassword = ConvertTo-SecureString $AdminUserPassword -AsPlainText -Force
$AdminUserCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $AdminUserName, $SecureAdminUserPassword

# 不需要手动输入就可以给 -Credential 参数提供管理员凭据
Start-Process -FilePath "powershell_ise.exe" -Credential $AdminUserCredential

直接明文也太危险了,有没有更安全一点方法?

用户凭据的密码在本机加密后再取用

首先先把密码给转成安全字符,然后再加密,加密后的密码只有这台计算机才能还原。

1
2
3
4
5
6
7
8
9
10
# 输入密码转成安全字符串
$SecureString = Read-Host "请输入密码" -AsSecureString
# 不想弹窗输入也可以用 $SecureString = ConvertTo-SecureString -String "PassowrdText" -AsPlainText -Force 但是不建议

# 安全字符串转换为加密的标准字符串
$EncryptedStandardString = ConvertFrom-SecureString $SecureString

# 加密的标准字符串导出文本文件
$EncryptedStandardString | Out-File -FilePath "D:\EncryptedPassord.txt"

然后到用的时候

1
2
3
4
5
6
7
8
9
10
# 密码文件路径
$EncryptedPassordPath = "D:\EncryptedPassord.txt"

# 管理员凭据
$AdminUserName = "$env:COMPUTERNAME\Administrator"
$SecureAdminUserPassword = Get-Content -Path $EncryptedPassordPath | ConvertTo-SecureString
$AdminUserCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $AdminUserName, $SecureAdminUserPassword

# 不需要手动输入密码就可以给 -Credential 参数提供管理员凭据
Start-Process -FilePath "powershell_ise.exe" -Credential $AdminUserCredential

由于 ConvertTo-SecureString 接受的就是字符串,可以不从文件文件获取,直接写在脚本里也行,例如:

$SecureAdminUserPassword = ConvertTo-SecureString -String "01000000d08c9ddf0115d111……e98c0ab0f1a9f2e251611f9"

但是不推荐这样,你把密码文件分离开来的话,放在共享文件服务器还是什么别的地方,还能用访问权限来保护一下。
反正就是一串字符,能存字符就行,拆开放也行,再加密也行,可以发散一下思路来增加授权限制来间接保护一下,写死在脚本直接一锅端。

安全是安全了,就算加密文件丢了,不在对应的电脑上解密也用不了,但是我又不想每台电脑都生成一次,能不能通用一点?

用户凭据的密码以密钥加密后再取用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 生成一个随机的Key,随便把导出成文件
$key = New-Object Byte[] 32
[System.Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($key)
$key | Out-file "D:\AES.key"

# 输入密码转成安全字符串
$SecureString = Read-Host "输入密码" -AsSecureString
# 不想弹窗输入也可以用 $SecureString = ConvertTo-SecureString -String "PassowrdText" -AsPlainText -Force 但是不建议

# 安全字符串转换为加密的标准字符串,以Key来加密
$EncryptedStandardString = ConvertFrom-SecureString $SecureString -key $key

# 加密的标准字符串导出文本文件
$EncryptedStandardString | Out-File -FilePath "D:\EncryptedPassord.txt"

-key 参数本质上也是字符串数组,也可以直接写进脚本里面,但也是不推荐这样的。

$Key = (3,4,2,3,56,34,254,222,1,1,2,23,42,54,33,233,1,34,2,7,6,5,35,43)
$StandardString = ConvertFrom-SecureString $SecureString -Key $Key

ConvertFrom-SecureString 也可以不用 -key 参数,用 -SecureKey,但按文件分开存放的增加限制的思路的话,其实区别不大……

然后到用的时候

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Key文件路径
$KeyPath = "D:\AES.key"
# 密码文件路径
$EncryptedPassordPath = "D:\EncryptedPassord.txt"

# 管理员权限
$AdminUserName = "$env:COMPUTERNAME\Administrator"

$key = Get-Content -Path $KeyPath
$EncryptedPassord = Get-Content -Path $EncryptedPassordPath
$SecureAdminUserPassword = ConvertTo-SecureString -String $EncryptedPassord -Key $key
$AdminUserCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $AdminUserName, $SecureAdminUserPassword

# 不需要手动输入密码就可以给 -Credential 参数提供管理员权限
Start-Process -FilePath "powershell_ise.exe" -Credential $AdminUserCredential

把脚本打包成 exe

除了上面这几中加密分开存放的方面外,还有一个方法可以增加一定的安全性,把脚本打包成 exe。

之前我在 PowerShell 入门教程推荐和常用代码片段 - tjxwork (tjxblog.com) 里面有提到一个 PowerShell 开发工具

SAPIEN PowerShell Studio 强大的 PowerShell GUI 设计器和脚本调试器 (sapien.com)

这个软件可以把 PowerShell 脚本打包成 exe,也可以嵌入凭据,打包出来的 exe 可以按嵌入的凭据来直接运行,他们说嵌入的凭据是加密的。

但有没有脚本内容有没有额外的加密我就没留意了,但总的来说光打包成 exe 这个就能拦住大部分人了,组合起来使用能相对更安全一点。

参考

淺談 PowerShell 中的密碼字串加密處理 - 黑暗執行緒 (darkthread.net)

PowerShell 脚本中的密码 - sparkdev - 博客园 (cnblogs.com)

ConvertFrom-SecureString (Microsoft.PowerShell.Security) - PowerShell | Microsoft Learn

ConvertTo-SecureString (Microsoft.PowerShell.Security) - PowerShell | Microsoft Learn

补充

不管怎么说,把通用的管理员凭据,写入一个或者多个文本文件里面,在普通用户下运行来自动使用。

这种行为总是不太安全的,各种方法混合验证来使用会相对的更安全一点。

毕竟 SecureString Class 安全字符串 这个东西本来就不是非常安全的选择。

平台兼容 / DE0001:不应使用安全字符串 (github.com)


原文作者:tjxwork
原文链接:https://www.tjxblog.com/blog/2023-0005
发布时间:2023-02-15